Getting Started

TecStock's Formula System (TSFS) is a technical analysis tool that is powerful, flexible, yet easy
to use.
TSFS can be used to design user's custom indicators, stock
screeners and produces high quality stock charts.

In TSFS, a formula can be as simple as just consisting of one word like the following
example
shows. The formula in the example
displays a curve connecting all the closing prices in the chart.

As you can see in the examples above, TSFS uses self-explanatory
keywords to refer to market data like
the open price, the closing price, the trading volume etc.
As you have probably expected, ma is a built-in function that calculates the
simple moving averages (some other tools use sma
to refer to simple moving averages. In the terminology of TSFS, sma
is used to indicate a special type of weighted moving averages. You will learn more about that later).
ma(close,10) is the 10-day moving averages of the
closing price. Also, TSFS uses the arithmetic operators in a natural way
as everyone would expect.
Because of this, in many cases formulas in TSFS are easy to understand
and do not require any further explanations.

On the other hand, TSFS has powerful features that even simple formulas,
only a few lines long, can produce complex results.
The following short formula
implements the standard indicator of
MFI (Money Flow Index).

You may have noticed that all the colors used in the chart are specified in the
formula. This implies that you can choose whatever color you want for
your custom indicators. We will show you later that you have much more control
over the chart, from colors, to line styles, icons, string texts etc.

Why TSFS

Tecstock.com covers
more than 10,000 stocks trading in the North America stock exchange markets.
Without computerized tools, it is impossible to us to monitor stock markets everyday.
There are quite a few commercial and free stock screeners available on
markets. While these stock screeners are useful under certain circumstances,
they all share many of the same limitations:

The screeners can only use standard indicators and predefined criteria
to scan stocks. It is not possible to users to
scan stocks using their custom indicators and patterns to meet special needs.

Many of the screeners only show results for the current date.
Users seeking for stocks with trading opportunities showed up in the recent past
can not find their targets.

The screeners are all lacking means for visually plotting signals
on high quality charts.

TSFS is to provide a platform with the
basic functions necessary for the technical analysis. By combining basic functions
it provides users with unlimited possibilities to explore
their trading ideas.
In addition,
it seeks to overcome potential shortcomings in many of the existing
stock screening systems.

What Can TSFS Not Do

Even it is well known, we want to emphasize that stock markets
are unpredictable and full of risks.
TSFS is a computerized technical analysis tool. TecStock.com uses TSFS to scan
and monitor stock markets using your
formulas and report the results to you everyday.
No matter how exciting the scan results seem to be, doing the
fundamental analysis is always a must before
any trading activities. Formulas alone can not and should not be used to
make any kind of trading decisions.

Exploring TSFS

The way you really learn TSFS is to put your hands on and
write formulas with it. TecStock.com has created an easy-to-use GUI for
editing
and testing formulas.
As you go through this guide, we encourage you to try out the formula
system features as you learn about it.

If you already have a user account, log on now. If this is the first time
you try TSFS, you may want to sign up for a trial account for free.

Before we go editing and testing formulas, let us first understand how
formulas are organized in TSFS.

Formulas in TSFS are organized in a tree-like list structure. The root node
of the tree list is Formulas, followed by two subnotes Sys Formulas and
My Formulas (which is visible only when you have signed in and have
defined some formulas in your account).
All the TSFS predefined formulas are listed under the node Sys Formulas,
while your custom formulas are shown under My Formulas.

Under both nodes of Sys Formulas and My Formulas,
formulas are organized so that they are grouped in two primary
categories: In Subchart and In MainChart.
As the name indicates,
formulas under the node In MainChart
will be displayed inside the main stock chart, while formulas under the node In Subchart
will be displayed either above or below the main stock chart.

In each of these two categories, formulas are further grouped together by the
group name if the group name is available. Formulas
without any group names are simply listed below the nodes of In Subchart
and In MainChart.

You can try out the formula list in the left part of
the page editing
and testing formulas.
Some common standard indicators predefined by TSFS are without group names.
It has been done so that they can be easily referred to without having to check
group nodes every time.

You saw a couple of formula examples in previous sections. Suppose you want to
write a new formula representing an average system as the following:

ma10: ma(close, 10);
ma30: ma(close, 30);
ma50: ma(close, 50);

The formula calculates the 10-day, 30-day and 50-day moving averages of
the close price and displays the results with names
ma10, ma30 and ma50, respectively.

Copy the formula above,
go to the page editing
and testing formulas, click on the "New Formula" button
and paste the formula to the text area below the
parameter fields as shown in the following figure.

Before you can test this formula, it is mandatory to provide a formula name.
Type in MyMA as the formula name in the name field on the top.
You may also want to give your formula a group name so that it will be
listed together with other formulas having the same group name.
In this example, you use Average System as the group name.

Note that both formula name and group name are case insensitive. While
the formula name should not include any spaces, the group name
containing spaces is perfectly valid.

Optionally, you can provide a full name for your formula and put
whatever you want as the explanation text in the description
field for your formula.

There is no parameters in this formula. So leave all the parameter fields blank.
There is an In MainChart check box beside the parameter fields.
This is to indicate whether or not the formula should be plotted in the
main stock chart. While some indicators like MA (Moving Averages),
BOLL (Bollinger Band), ZIG (Zig Zag)
are always displayed inside the main
chart, other indicators like RSI(Relative Strength Index),
MACD(Moving Average Convergence Divergence) etc.
are only plotted in subcharts.
Because MyMA is kind of moving average indicators,
you may want to check the box so that MyMA
will be displayed inside the main chart. You may uncheck the
box later to see how MyMA is displayed outside the main chart.

Type in a stock symbol you want to test against,
for instance IBM, in the symbol field in the middle of the screen,
as shown below.
Then click on the Test button. Now you should see your custom indicator
displayed with the name MyMA inside the IBM chart.
Because we did not specify any colors in our formula, default colors are
automatically assigned to the three curves.

If you uncheck the In MainChart box and click on the
Test button again,
the MyMA indicator will be plotted inside a subchart.

While exploring formulas, you will probably write code that does not work.
Let's modify the formula
above and change the word "close" to "clsoe" in the second line.
Click on the Test button.
An error message in red will show up on the top of the screen:

Line:2, Column:11: Invalid syntax: undefined symbol 'CLSOE'

see the figure below. This indicates
that at character position 11 in the second line, the symbol 'clsoe' is not defined.
Correct the word and click on the Test button, the formula should work again.

When you are satisfied with your formula, it is time to save it. Before you
can do this, you will be asked to sign in.

The formula MyMA does not use any parameters.
TSFS does support formulas with parameters.
Take a look at the following formula. It uses another build-in function ema
to calculate exponential moving averages of close prices.
Variables p1, p2, p3 are not defined in the formula and will be used as parameters.

ema1: ema(close, p1);
ema2: ema(close, p2);
ema3: ema(close, p3);

Copy the formula above,
in the page editing
and testing formulas, click on the "New Formula" button,
and paste the formula to the formula text area; put
formula name, group name and parameters as shown in the figure below.
Please note that when specifying a parameter, the parameter name, its
default value, the minimum and the maximum value have to be defined.
The parameter name is case insensitive and can not include any spaces.
The default value has to be in the range of the minimum value and the maximum
value.

Now click on the Test button, three exponential moving averages of close prices
will be displayed in the main chart with their default parameters.
If you have already logged in, click on the Save
button. You will notice that the formula list on the
left side of the screen is updated. Both MyMA and
MyEMA formulas are now grouped together under the node
Average System in the subtree as shown
in the figure on the left side.

Note that the tree-like formula list displayed in the left figure does not have the
branch In SubChart under the node
My Formulas. This is because in this particular user
account,
we have not defined any formulas yet that should be displayed in subcharts.

About Backtesting

The facility of backtesting is designed to help you evaluate and
tune the performance of your custom formulas.

You can backtest your formulas against different stocks by choosing
different price and market cap ranges. In addition, you can
explicitly specify stocks you want to include in the backtesting
process. The check box beside allows you to tell if you want to test
your formula only against the stocks you have specified.

Depending on the complexity of formulas, online backtesting may
demand a significant amount of computational power and thus the
total number of stocks used in each backtesting is limited to 100.
Time frame used in backtesting varies from 2 months to 2 years,
depending on the number of stocks used in the test. The less stocks
you specify, the bigger the time frame will be.

Before back testing, make sure that your formula contains an expression like

singal_signame : ...

This tells TSFS that your formula is going to generate signals with
the name signame.

For example, the following formula generates
doji signals when doji
patterns occur.
A doji pattern is identified when the close
price is the same as the open price and the
high price is not equal to the low price.

signal_doji : close = open and high != low;

Once you have finished tuning and saved your formula,
TecStock.com will use it to scan more than 10,000 stocks
everyday after market close and generate signals for you.

Case Insensitivity

TSFS is a case-insensitive system. That means that
keywords, variables, function and formula names
and any other identifiers are not case-sensitive.
The keywords close, Close,
CLOSE are all
refer to the closing price. A formula with the name MyMA
can be referred
to as myma or myMA.

The formula below calculates the average value of open and close
and assigns the result to the variable VAR. The variable is then
referred to as var in the following two lines. The formula
displays
the 20-day and 30-day moving averages of VAR.

VAR := (close + open)/2;
ma20: ma(var, 20);
ma30: ma(var, 30);

Comments And Semicolons

In formulas,
any text between the # character and the end of the line is
treated as a comment and is ignored by TSFS.
In addition, any text between the
characters /* and */ is treated as a comment as well. These comments may span
multiple lines but may not be nested.

Statements in formulas have to be terminated by semicolons(;).
Semicolons serve to separate statements from each other.
It is a syntax error if the semicolon is missed.
You can put multiple statements in one line. This is valid
as long as each statement is terminated by a semicolon.

The formula below contains two comments. It also has two statements
sitting in the same line. The formula compares the
difference between ma (moving averages)
and ema (exponential moving averages) by
showing
both ma(close, 10) and ema(close, 10) in the main chart.

/* This is a comment:
In the following, two statements are in the same line. This is valid
because each statement is terminated by a semicolon.
*/
ma10: ma(close, 10); ema10: ema(close, 10); # this is again a comment

Single Numbers and Arrays of Numbers

TSFS is very easy to work with. It
only supports one data type: numbers.
Numbers are used either as single numbers
or as a collection of numbers, often referred to as an array of numbers.

We have already seen the use of numbers many times so far. For example,
ma(close, 10), where 10 is a single constant number.
What we have not mentioned yet is that because every trading day has a closing price,
close actually refers to an array of numbers rather than a
single number. ma(close, 10) returns the 10-day moving averages
of the closing price for each trading
day. Thus, ma(close, 10) is an array of numbers as well.

The formula below calculates the 10-day bias, a stock fluctuate measurement
commonly used in technical analysis. Obviously, the expression
(close - ma(close, 10))/ma(close, 10) * 100
returns an array of numbers.
The formula also uses 0 as a constant to plot the zero-line.
When
displaying
the formula,
the constant value 0 remains unchanged in the whole time frame defined in the chart, while
the value of bias is changing in every trading day.

constant : 0;
bias : (close - ma(close, 10))/ma(close, 10) * 100;

Formula Names And Parameters

We have so far seen a couple of formula examples. A formula is in fact a collection of
statements. A statement can be an expression for calculation, like
(open + close)/2; or a function call that does not do any
calculation but plotting, for example,
vertline(close > open);
which plots a vertical line in the chart when the condition close > open
is true.

We have seen that
each formula has to have a name.
A formula name starts with a letter,
followed by letters, numbers, or underscores.

Formula names are unique for each user. What it means is that you can not have
more than one formulas with the same name in your account. If you try to
save the second formula using the name of your first formula, the first formula
will be overwritten. However, you can use formula names already used
by other users or by the system. There will not be any name conflict.
You can, for example, write a formula with the name MACD, although
TSFS has predefined a formula with this name for the standard MACD indicator.

We have seen the formula MyEMA in the previous chapter with three
parameters.
Internally, a formula can have unlimited number of parameters. To avoid overusing
this feature, the page editing
and testing formulas only allows up to 4 parameters to be used in each
formula, which should be sufficient in most cases.

When using parameters in formulas, it is mandatory to provide parameter name,
the default value, the minimal and the maximum value for each parameter.
Parameter names follow the same rule as formula names. They have to
start with a letter, followed by letters, numbers, or underscores.
Parameter names have to be unique in formulas. If, for example, P1
is used as the name of a parameter, other parameters and variables in the same formula are
no more allowed to use P1 as their names.

The default value of parameters are the value used by TSFS when formulas are called without parameters.
The default value should be always in the range formed by the minimal and the maximum values.

Variables

In formulas variables can be used to store constants and
expression results.
By storing expression results in variables, we
can avoid calculating the same expressions multiple times and thus
improve the performance of TSFS.
Like formula names, a variable name starts with a letter,
followed by letters, numbers, or underscores.

Variables can be either of external type or of internal type.
External variables are accessible outside formulas (we will cover
this when we talk about calling formulas from other formulas).
When displaying a formula, values of external variables will be
plotted and variable names will be shown in the chart.
External variables are defined through the assignment operator ":".
For instance,

Internal variables, on the other hand,
are used inside formulas to hold intermediate results.
Internal variables are not accessible from outside. When displaying
formulas, values of internal variables will not be shown in charts.
Internal variables are defined through the assignment operator ":=".
For example,

var := ma(close, 20); # var is defined as an internal variable

Note that you can define a variable only once, i.e., you can not assign
values to a variable multiple times. The formula below is not valid:

Unless drawing functions are used, a formula has to have at least
one external variable (including anonymous variables). Formulas that consist
only of internal variables are doing nothing when viewed from outside and
are thus not valid. The following formula has only two interval variables defined.
It is useless and invalid because none of its variables
is accessible outside the formula, and when displaying the formula, nothing
would be shown in charts.

# invalid formula
var1 := ma(close, 10);
var2 := ma(close, 20);

The following formula is an implementation of the standard indicator
ROC (Price Rate-of-Change).
ROC is a measurement of fluctuate as percentages around
the zero line.
In the formula, internal variables m and n are defined to store constants 6 and 12;
the constant 0 is used without any variable name. In this case, TSFS will create an
anonymous variable to hold its value. We will talk about this later.
The internal variable l_roc is used to store the fluctuate percentage, where the function
ref(close, n) refers to the close price 12 days
ago (recall that n is equal to 12).
l_roc is then followed by a description which
specify the color and the line thickness.
Again, l_roc is used without variable name and
TSFS will create an anonymous variable
to hold its value. The last statement calculates the 6-day
moving averages of l_roc (recall that
m is set to 6) and assigns
the result to the external variable ma.
When display the formula, it looks like
this.

As you have seen above, when constants or expression results are not explicitly
assigned to any variables, TSFS will create anonymous variables to store
their values. Anonymous variables are always external. This means that
values of anonymous variables will be plotted in charts. Because anonymous
variables do not have names, values stored in anonymous variables can not
be reused and when displaying the formula, no name but values will be shown in charts.

In the above example, the zero line is plotted because of the anonymous
variable holding the constant 0. The value
0.00 is displayed in the chart without any name. Similarly, l_roc is
stored in an anonymous variable and thus get displayed without any name.
The name "ma" is shown in the chart because ma is an external variable.

Variable Names For Signals

There is a special case about variable names. When TSFS monitors markets using your
custom formulas, variable names in your formulas prefixed with
signal_ like

signal_yoursigname : ...

will trigger TSFS to generate signals with the name yoursigname
when the value of the variable is true (i.e., not zero).

For example, the following formula generates
filled_black_candles signals when
one-day candlesticks characterized with a long black body having no
shadows on either end are found (Filled black candle, also referred to
as black marubozu, is an extremely strong bearish candlestick pattern).

range := high - low;
signal_filled_black_candles : open = high and close = low and range > ma(range, 10);

Calling Formulas From Other Formulas

It is important to be able to reuse formulas we have already defined when writing new formulas.
The formula reusability will help us keep formulas small, simple, flexible and independent.
We will not have to start from scratch every time when we write new formulas.

TSFS uses the following syntax to allow you call formulas from other formulas

"formulaname.varname"(param1, param2, ...)

where formulaname is the name of the formula you want to call,
varname is an external variable name defined in
formulaname, and
param1, param2, ... are the formula parameters.
It tells TSFS to execute the formula formulaname using the
given parameters param1, param2, ... and returns the result
stored in the variable with the name varname.

If the formula formulaname does not have any parameters,
then the syntax of calling formulaname will be

"formulaname.varname"

Note that you can use the above syntax to omit parameters even if
formulaname has parameters.
In this case, because no parameters are explicitly given,
TSFS will use the default values of parameters.

You can also omit varname and call formulaname
in one of the following ways

"formulaname"(param1, param2, ...)
"formulaname"

This will return the result stored in the last external variable
in formulaname. Note that the last external variable
can be an anonymous variable.

So far enough about the syntax. Let's have some examples.

Recall that in the previous chapter, we have defined the formula
MyMA

ma10: ma(close, 10);
ma30: ma(close, 30);
ma50: ma(close, 50);

and the formula MyEMA with parameters
p1,p2,p3

ema1: ema(close, p1);
ema2: ema(close, p2);
ema3: ema(close, p3);

Now you can reuse these two formulas by calling them from your new
formula like the following example shows

In the example above, "myma.ma10" in the first line executes the formula
MyMA and returns the result stored in ma10.
Thus, a1 contains the 10-day moving averages of the closing price.
In the second line, "myma.ma50" executes the formula
MyMA and returns the result of 50-day moving averages to
a2. The formula call in the third line omits the variable name.
According to the syntax discussed above, it returns the result stored in the last
external variable in MyMA which is ma50.
Thus, "myma" is equivalent to "myma.ma50"
and
a2 and a3 contain the same result.

You may have noticed that the first 3 lines in the example require the execution of
the same formula three times. Is this really necessary? No,
you are absolutely right, it is not.
Internally, TSFS will detect that the first three lines execute
the same formula with the same parameters, in this case no parameters, and will thus
process the execution only once when the first line is encountered, and will reuse the results
in the second and third lines.

Similarly, "myema.ema1"(50,100,200) calls the formula
MyEMA with the parameter 50,100,200
and returns 50-day exponential averages to b1.
"myema.ema3"(50,100,200) returns 200-day exponential averages.
"myema(50,100,200) is equivalent to
"myema.ema3"(50,100,200) because ema3
is the last external variable in
MyEMA.

The last three lines call MyEMA without providing any parameters.
So default values of parameters will be used. Recall that in the previous chapter, we defined the
default parameters as (5,10,20). It returns
5-day exponential averages to c1 and
20-day exponential averages to both c2 and c3.

Please note that when using the above example to analyze stock, say XYZ,
all the formula calls return results regarding to the stock XYZ, i.e.,
"myma.ma10" will return 10-day moving averages of the closing price
of XYZ, "myma.ma50" will return 50-day moving averages of the closing price
of XYZ, and so on.

In technical analysis, however, it is a common practice that we want also to
take other stocks, often
market indices into account. For instance, before generating buy signals for stock XYZ, we want to
check if the market is under bullish condition. We want to generate buy signals only
when the market is bullish.

To make this possible,
TSFS provides another formula call syntax to return results in regard of a given stock or index:

"ticker$formulaname.varname"(param1, param2, ...)

which tells TSFS to execute the formula formulaname against the stock
ticker using the
given parameters param1, param2, ... and returns the result
stored in the variable with the name varname.

You can also use the following syntax variations to omit parameters or varname
when you want to use default values of parameters if any,
or you want to return the result
stored in the last external variable in formulaname:

"ticker$formulaname.varname" # use default values of params if any
"ticker$formulaname"(param1, param2, ...) # return the result in the last external variable
"ticker$formulaname" # use default values of params if any and
# return the result in the last external variable

The formula makes use of comparison and logical operators that will be covered in the next section.
The idea should be obvious. It
compares different moving averages (MA) and sets the bullish condition cond
as true when the 50-day MA is above the 200-day MA and the 10-day MA is above the
50-day MA and the price is closed above the 10-day MA.

Tecstock.com uses Yahoo's ticker symbol convention.
So ^IXIC refers to the Nasdaq Composite Index,
while ^DJI is the symbol for Dow Jones Industrial Average Index.

In the formula, "^ixic$bullish.cond" returns the
cond value of Nasdaq index to nasdaq_cond,
"^dji$bullish.cond" returns the
cond value of Dow Jones Industrial index to nasdaq_cond,
cond stores the condition value of the current stock.
Thus, if you apply the formula to stock XYZ,
the variable buy
will be set to true only if XYZ, the Nasdaq index and the Dow Jones Industrial index are all
under the bullish condition.

It should be pointed out that this example is for demonstration purpose only.
Bullish does not necessarily represent a precise
criteria for checking the bullish condition of markets.

When you call an existing formula from your formulas, you have to make sure that

the formula you are calling does exist and is accessible from your formulas.
You can access your own formulas and those predefined by TSFS. Formulas
defined by other users are not accessible;

you are referring to an external variables in that formula;

the parameter values you pass over are in the ranges formed by
minimum and maximum parameter values of that formula;

if you call the formula in regard of a given stock, the stock symbol
exists in tecstock.com.

you do not call formulas recursively. TSFS does not allow any direct or indirect
recursive formula calls. You can not call a formula from itself. If you have
formulas F1, F2, F3, and F1
calls F2, F2 calls
F3, then
formula F3 can not call F1.

Arithmetic Operators

All the basic arithmetic operators that you are familiar with are supported in TSFS.
This includes addition, subtraction, multiplication, division, and modules.
The expression

(open + close)/2;

for example,
is calculated in the way as you would expect: it first calculate
open + close, the result is then divided by 2.
Note that operator precedence has caused the open + close
to be executed first.

While this seams easy and simple, what really happens behind the scene is that
open + close is calculated for every
trading day. The result is an array of sums of
open and close.
Each sum in the array is then divided by 2.
The final result is an array of average values of
open and close.

Comparison And Logical Operators

TSFS also supports the following comparison and logical operators:

greater than

>

greater than or equal to

>=

less than

<

less than or equal to

<=

equal to

=

not equal to

!=

<>

logical and

and

&&

&

logical or

or

||

|

Comparison operators can be used to construct boolean expressions,
for example,

close > open;

A boolean expression returns either true or false.
TSFS uses 1 (one) to refer to the "true"
boolean value, 0 (zero) to
refer to the "false" boolean value. In the above example,
because the close and the open price are compared
in every trading day, the
expression close > open
returns an array of numbers containing 0s and 1s.

Boolean expressions can be conjuncted using the
logical operators AND or OR to express complex
conditions. For
example,

where is_up1 is set to 1
only when the price is closed above both the open price and
the previous closing price, while
is_up2 is set to 2
(not 1 because of the multiplication of 2)
as long as the price is closed above the open price.
is_up2 is multiplied by 2 because we
want to see the difference
between is_up1 and
is_up2 more clearly in the chart.

Numbers can be conjuncted with other boolean expressions as well. In this
case, all the non-zero numbers will be treated as true,
while zero numbers will be treated as false.

For the sake of convenience, you can use "||" or "|" to replace
AND and use "&&" or "&" to replace
OR in your formulas.
For example,

is_up_1 : close > open or close > ref(close, 1);
is_up_2 : close > open || close > ref(close, 1); # is_up_2 is equivalent to is_up_1
is_up_3 : close > open | close > ref(close, 1); # is_up_3 is equivalent to is_up_1
is_up_4 : close > open && close > ref(close, 1);
is_up_5 : close > open & close > ref(close, 1); # is_up_5 is equivalent to is_up_4

In the following, we will make use of comparison and logical operators to
define a formula that can be used to identify the
candlestick pattern "morning star".

The morning star is a major bottom reversal pattern formed by
three-candlestick:

a long-bodied red candle extending the current downtrend,

a short middle candle that gaped down on the open,

and a long-bodied white candle that gaped up
on the open and closed above the close of the first day.

The first long red candle shows the continuing bearish nature
of the market. Then the short middle candle appears
implying the incapacity of sellers to drive the market lower.
The last long white candle shows that the market turned bullish now.

In the description above, what "a long-bodied red candle" really means
is that
on the first day, the market was down, close < open, and the difference
between close and open was big;
"short middle candle" means that on the
second day, the market was either up or down and the difference between
close and open was small;
"a long-bodied white candle" means that
on the third day, the market was up, close > open, and the difference
between close and open was big.

Let us first define a list of variables as shown below, where
abs(close-open) is a built-in function that returns
the absolute value of close - open.
The function llv(low,60) returns the lowest low
price within the last 60 days.

is_up := close > open; # is_up is 1 when the market is up, 0 otherwise
is_down := close < open; # is_down is 1 when the market is down, 0 otherwise
diff := abs(close - open); # the absolute value of the difference between
# open and close
diff_ma := ma(diff, 10); # 10-day average of diff
is_dtrend := low = llv(low, 60); # is_dtrend is 1 if the low is the
# lowest low in the last 60 days.

Because there is no absolute measurement
for when the difference between open and close is big and when it is small,
we compare the difference to its 10-day average values.
The difference is considered to be big if it is greater than
its 10-day average, i.e., diff > diff_ma;
it is considered small if diff < diff_ma/2.
We also assume that the market is in its downtrend if the
low price is the lowest low price in the last 60 days.

According to the definition above, a morning star pattern consists
of three candlesticks. When trying to identify the pattern, the first
candlestick is actually the candlestick two days ago, the second
candlestick is that on the previous day and the last candlestick is the
current one.

We have seen that ref(close, 12) refers to
the 12-day-ago closing price. In general, we can use
ref(x,n)
to refer to the
n-day-ago value of x.

The following code lists conditions each candlestick has to fulfill.

# The first candlestick has to fulfill the condition 1:
cond1: ref(is_down, 2) and # 2 days ago, market was down
ref(diff > diff_ma, 2); # 2 days ago, the difference was big
# The second candlestick has to fulfill the condition 2:
cond2: ref(diff < diff_ma/2, 1) and # 1 day ago, the difference was small
ref(open, 1) < ref(low, 2) and # the open 1 day ago was below the low 2 days ago,
# i.e., it gapped down on the open 1 day ago
ref(is_dtrend, 1); # 1 day ago, the low was the lowest low
# in the last 60 days.
# The third candlestick has to fulfill the condition 3:
cond3: is_up and # market is up on the current day
diff > diff_ma and # the difference is big
open > ref(high, 1) and # the current open is above the high 1 day ago,
# i.e., it gapped up on the open on the current day
open < ref(open, 2) and # current open is below the open 2 days ago
close > ref(close, 2); # current close is above the close 2 days ago

After connecting all the conditions using the logical operator
AND and putting all the code fragments
together, we get the following formula.
Notice that when the morning star pattern is identified, the formula marks the
pattern with a small red flag below the low price using the function
drawicon(cond,low,7).
The formula identifies three morning star patterns in daily charts of
CHI,
STLD
and X.

Notice that what has been shown is just one possible
implementation of "morning star". You may
define diff_ma as ma(diff, 20)
or modify other conditions to implement your variation of "morning star".

Functions

TSFS provides a set of basic functions necessary for the technical analysis.
The functions are to serve as the foundation of TSFS.
By combining these basic functions you get boundless flexibility to explore
your great ideas and produce high quality charts.

This chapter discusses each of the TSFS functions in detail.
They are arranged by category.
For the sake of easy reference, an alphabetical index of functions are
provided at the end of the guide.

Market Data

open,
close,
high,
low,
volume

These are the five basic market data
in the technical analysis which, for a serial of trading periods,
refer to the period opening price, the period closing price,
the highest and the lowest prices and the total trading volume
in each period.
A period is not necessarily always a day, it can also be
a week (in weekly charts, for example), a month (in monthly charts),
or an 1-minute or 5-minute trading period (in intraday charts).

open, close, high, low, volume are the market data
most often referred to in technical analysis. For the ease of reference,
each of them has
short form acronyms as shown in the table below.

keyword

open

close

high

low

volume

acronym

o

c

h

l

vol, v

In the following, we are going to provide more examples to demonstrate the use of the market data
and their acronyms.

Example: Moving averages of closing price like
ma(close, 50) and
ma(close, 200)
are commonly used in technical analysis to
help extract the stock short term and long term trends. These moving averages only consider
the closing price. Some investors believe that it might be more precise to also
consider the open price. Some of them even want to take the high and the low
prices into consideration. You can use the following formula to
compare the differences between these approaches.

Example: The following formula plots three volume average lines on the chart. In the formula,
volstick is a special descriptor
in TSFS used to draw
volume sticks, color.. is
for specifying the color for each average line.

Example: Investors are used to use RSI (Relative Strength Index) to measure
the strength of stocks. The following formula tries to do the same thing but from a
different perspective.
It makes use of triple emas (Exponential Moving Average)
to filter out the noisy data of
(close+high+low)/3 and calculates
the percentage of change compared to the previous day. The value of
control is around the 0-line. The higher the
absolute value of control is,
the stronger the stock is under the control.
To let you compare the results,
both the formula and the standard RSI
are plotted on the chart.

The keyword capital refers to the shares float, the number
of shares held by the public and available for trading.
capital is a single number and is
often used to check stocks' trading
liquidity in the market.

The trading volume
is one of the most important factors in technical analysis. Many
investors believe that volume precedes price, meaning that price change
pressure shows up in the volume figures before presenting itself as a
price trend reversal.
In many cases,
however, the trading volume can not be used to compare
the trading liquidity across stocks.
A trading volume of 10,000,000 may be huge for some stocks
but can be just a small fraction for others.
The next example implements a new volume indicator PVOL. It makes use
of capital and compares
volumes in an unified way. It calculates the volume percentage of capital
and so makes comparison of the trading liquidity across stocks possible.

For instance, a stock XYZ with a capital of 100M (100 millions)
was trading with the volume 5M. The volume percentage of capital
will be 5%. A stock ZYX with a capital of 1,000M
was trading with a much higher volume 10M. Because its
volume percentage of capital is just 1%,
stock XYZ has a much better trading liquidity than stock ZYX.

Unfortunately, for some reason
capital is not always available. TSFS defaults
capital to zero if it is not present.
PVOL uses the built-in function
if(..) to check
the availability of capital.
If capital is not available, i.e.,
capital = 0, it
simply returns volume
and the formula thus behaves just like a regular volume indicator.
Otherwise it calculates
the percentage of volume/capital * 100.
Here are some daily charts with the new volume indicator PVOL:
IBM, MSFT, SNDK.

PVOL is predefined in TSFS and can be found in the formula list
under nodes: Formulas -> Sys Formulas -> In Subchart, Formulas -> Sys Formulas -> In Mainchart.

finance("data_name")

data_name:

financial data name.

TSFS preserves the keyword finance to refer to
a set of financial data. Currently the following data names are supported.
Please notice that not all the financial data listed below are available for
all the stocks. In case the data is not present, finance
will return 0.

shares_outstanding

the number of shares outstanding.
Shares outstanding refers to the number of shares of common stock
currently outstanding, the number of shares issued minus the shares
held in treasury.

shares_float

the number of shares float.
This is the number of freely traded shares in the hands of the public.
finance("shares_float") is equivalent to
capital.

Date Functions

This section explains functions that can be used to process date related calculations.

day,
weekday,
month,
year

The function day returns an array of numbers of the day in month from 1-31.
For
example

d : day;

The function weekday returns an array of weekday numbers
from 1-7 (Sunday = 7). For
example

wd: weekday;

The function month returns an array of month numbers from 1-12. For
example

m : month;

The function year returns an array of year numbers like
1998, 1999, 2000 etc. For
example

y : year;

Example: In the formula below, if the year of the current trading day is not the same as that
of the previous trading day, i.e., the current day is the first trading day in
the new year, it draws a happy face on the chart.

drawicon(year != ref(year, 1), low, 1), align1;

date

Function date returns an array of integers
representing dates. For dates before 2000-01-01,
it returns integers in the format YYMMDD. For any other dates, the returned integers have the
format 1YYMMDD. For example, for dates 1999-12-30, 2000-01-01, 2001-12-30,
date returns 991230, 1000101, 1011230 respectively.

Example: the following formula plots vertical lines
on the chart when the date is between 2016-11-19 and 2016-11-29.

vertline(date >= 1161119 and date <= 1161129);

If you have a set of well defined formulas and want to sell
your formulas in an encrypted format,
you can use date to restrict the use of
your formulas to a predefined time frame.

Example: the next formula is a variation of the KDJ indicator.
It has a restriction date defined inside and
works properly only until 2016-11-01.

this function takes two input dates as its arguments and returns
date1 - date2, the day difference number between
date1 and date2.

Example: this example shows the difference number between the current date and
the date 2016-10-01.

0, colorred;
diff : datediff(date, 1161001);

Notice that datediff(date1, date2) returns the
difference number between date1, date2
in terms of calendar day, i.e., numbers returned by
datediff(date1, date2) will include days
when markets were closed.

Example: if you want to get the difference number in terms of trading day, you can use the
built-in function barslast.
The formula below shows the difference
between calendar day diff and trading day diff, where
the function barslast(close > ma(close, 30)) returns
the trading day number since the last occurrence of
close > ma(close, 30).

Reference Functions

Reference functions
calculate data in the past periods in some way to
help process data in the current period.
We have met some reference functions like
ma,
ema,
hhv,
llv etc.
Reference functions are the most basic functions in TSFS and
are referred to in almost every formula.

count(condition, n)

condition:

an array of numbers representing boolean values,

n:

period number, it can be either a single integer or an array of integers.

The function counts the number of periods
in the last n periods (including the current period)
where the condition holds, i.e., condition != 0.
If n = 0, the function counts from the first period.

Example: the formula below counts the number of periods in the past 3 periods
(including the current period) where price was closed above its
5-day moving averages.

v1 : count(close > ma(close, 5), 3);

Example: the next formula is trying to do some kind of bottom fishing using the
9-day KDJ indicator.
The function hhv(high,9)
returns the highest high of the last 9 periods,
llv(low,9)
returns the lowest low of the last 9 periods,
k
is a percentage measuring how close the close price is to the bottom of the 9-day
trading range. k is smoothed to remove noisy values using
the function sma
and the result is set to the variable
d. If d < 20 (20%),
it is believed that the price is close to the bottom.

The formula uses two additional conditions to make sure that
the stock is in its uptrend and the volume is increasing.
So what the final condition cond is saying is
that for each period, set cond to 1 if
the price is closed above ma(close,3) at least
5% and the 3-day volume average is at least 50% larger than its
20-day average value and in the past 20 periods, the condition
d < 20
occurs at least once.

Example: the next formula compares the close price with the previous close price
ref(close,1) and calculates the percentage of periods
in the recent 60 periods where the stock price is up, down and in balance.

period number, it can be either a single integer or an array of integers.

The function sum returns the total sum of x
in the last n periods (including the current
period).
If n = 0, the calculation starts from the first
period.

Example: this is an implementation of the standard indicator
OBV (On Balance Volume).
OBV is designed to track changes in volume over time.
It is the running total of volume calculated in such a
way as to add the day's volume to a cumulative total if
the day's close was higher than the previous day's close
and to subtract the day's volume from the cumulative total
on down days. The assumption is that changes in volume will precede that in price trend.
OBV was created by
Joseph Granville and has a number of interpretive qualities and
should be used in conjunction with other indications of price trend reversals.

The formula recursively uses the built-in function if
and is simply consisting of one line code.
The if function has the general form
if(condition, a, b). It returns a
if condition is true and b otherwise.
The function sum is used with 0 as the period number, meaning that
it counts the total sum from the beginning.

Example: this example is a RSI-like indicator (Relative Strength Index).
It adds the
closing price difference from the previous period to
two different cumulative
totals depending on if the price is closed higher or lower than the
previous one. sum is used with the base of 24 periods.
The final result is the percentage of advanced cumulative totals.

Note that the function max has the general
form max(a,b). It compares
a and b
and returns the one with the greater value.

period number, it can be either a single integer or an array of integers.

ref(x, n) refers to the value of xn periods before the current period.
For instance,
ref(close, 1) refers to the previous closing price;
ref(ma(close, 50), 1) is the value of 50-period moving
averages of close in the previous period and
ref(ref(close, 1), 1) is
equivalent to ref(close, 2).

Example: this formula identifies the candlestick pattern where
the stock price is closed higher in 3 consecutive days.

period number, it can be either a single integer or an array of integers with n != 0.

ma(x, n) calculates the n-period
simple moving averages of x by adding
values of x from the last n
periods and dividing the result by n.

Suppose that a stock has the following
close prices in the last 3 trading days: $40, $41, $42, then for the
last trading day, ma(close, 3) = (40+41+42)/3 = 41.
In fact, ma(x, n) = sum(x, n)/n,
where n != 0.

Function
ma weights data from the last periods evenly and thus
is a lagging indicator, i.e., its result does not react
quickly to recent price changes.
Institutional investors are often using ma(close, 50) and
ma(close, 200) to
determine the market short and long trends.

Example: in this example, v1 is the 10-day moving averages of
close, v2 is the same as
ma(ma(c, 10), 10), while
v3 is identical to
ma(ma(ma(c, 10), 10), 10).

v1 : ma(c, 10);
v2 : ma(v1, 10);
v3 : ma(v2, 10);

ema(x, n)

x:

an array of numbers,

n:

period number, it can only be a single integer number.

ema(x,n) calculates the n-period
exponential moving averages of x. The exponential moving average
is a weighted moving average that puts more emphasis on the current period.
Assume ema(t) is the value of ema(x,a)
in the current period, x(t) is the value of
x in the current period,
ema(t-1) is the value of ema(x,a)
in the previous period, then

ema(t) = (2*x(t) + (n - 1)*ema(t-1))/(n + 1).

Example: we have mentioned that
ma weights data evenly in all periods and is thus
a lagging indicator.
Because ema put emphasis on the current period,
it reacts faster to recent price changes than ma.
The following formula demonstrates the difference between ma(close,10)
and ema(close,10).

# compare ma and ema
ma10 : ma(close, 10);
ema10 : ema(close, 10);

Example: MACD (Moving Average Convergence Divergence)
is a trend-following momentum indicator commonly used in technical analysis.
It shows the relationship between two
exponential moving averages of prices. MACD
is calculated by subtracting the value of ema(close,26)
from ema(close,12).
The 9-period exponential moving averages of the subtracting result is
then used as the signal line, functioning as a trigger of buy and sell signals.

sma(x,n,m) is another type of weighted moving averages. It is
calculated using the following recursive expression:

sma(t) = (m*x(t) + (n-m)*sma(t-1))/n.

where the time t indicates the current period, while
t-1 represents the previous period.

According to the definition, m can be any integer number
as long as m < n. In practice, however,
m is often set to 1. That means that sma
is often used to put more weight on previous periods. This is very different from
ema.
For this reason, when set m = 1, sma
becomes a very lagging function.

Example: the formula below compares the differences between ma, ema
and sma. You can see that ema(close,10)
reacts fast to recent price changes and is the least lagging indicator.
sma(close,10,1) moves far behind price changes and
is the most lagging indicator in the chart.

Example: the function sma has an important use in the standard indicator
RSI (Relative Strength Index).
RSI is an indicator for overbought/oversold
conditions. It is going up when the market is strong, and down, when the
market is weak. The market is deemed to be overbought once RSI
approaches the 70 level. If RSI approaches 30, it is an indication
that the market may be getting oversold.
In the following RSI formula,
function fillrgn
fills the region formed by rsi and horizontal lines
70 and 30 with the specified color,
polyline
is used to draw 3 horizontal lines.

Example: KDJ
is another example that uses sma.
KDJ is derived from the Stochastic indicator. It draws 3 lines
k,d,j and thus the indicator name.
The value of j can go beyond [0, 100] in the chart.
A negative value of j combined with
k,d at the bottom range indicates a strong oversold signal.
Likewise, when the j value goes above 100,
combined with k,d at the top range, it will
indicate a strong over bought signal.

dma(x,a) calculates dynamic moving averages of
x using the following recursive expression:

dma(t) = a*x(t) + (1 - a)*dma(t-1).

where the time t indicates the current period, while
t-1 represents the previous period.

dma(x, a) is a generic form of weighted moving average. In fact,
both
ema and
sma can be defined using
dma

ema(x, n) = dma(x, 2/(n+1)),
sma(x, n, m) = dma(x, m/n).

Function dma can be used to calculate
the market overall average holding cost, an important market fact
that is ignored by most stock analysts.
Consider the following formula
where cyc stores the value of the overall holding cost.

cyc : dma(close, volume/capital);

Suppose stock XYZ with a capital of 100M (100 millions)
has the initial price of $10 and is trading at this price on the first day.
To reveal the meaning of cyc
more clearly, let's suppose that for some reason it is trading
50% up and closed at $15 on the second day with a light volume of 1M.
Then the simple moving average ma of XYZ is

(10 + 15) / 2 = $12.50.

On the other side, since capital = 100M and
volume = 1M, according to the definition of
dma,

cyc = 1/100 * 15 + (1 - 1/100) * 10 = $10.05.

Because on the second day, only 1M shares of XYZ have been exchanged with
the price of $15, 99M shares of XYZ are still held with $10,
the overall average holding cost of XYZ is

(1 * 15 + 99 * 10) / 100 = $10.05

That is the same as cyc.
It should be clear to see that cyc
describes the real value of XYZ more precisely.
ma is inaccurate because it
does not consider trading volumes.

The concept of holding cost is very important. TSFS has predefined a formula
CYC that we will talk about
in the next chapter.

Example: cyc can be very different from ma.
This is specially true when the price is volatile with light volumes.
The following example demonstrates the differences between
ma(close,50), ma(close,200)
and cyc.

period number, it can be either a single integer or an array of integers.

Function hhv(x,n) returns the highest value of
x in the last
n periods (including the current period),
while function llv(x,n) returns
the lowest value of x in the last
n periods (including the current period).
If n = 0, the calculation starts from the first period.

Example: hhv and llv are often used
to determine the current price position in the trading range of the
last n days.
In the following formula,
rsv indicates how close the current closing price is to the
bottom of the 9-day trading range.

These two functions counts period numbers in the past since the period
where hhv(x,n) and
llv(x,n) occur, respectively.
If hhv(x,n) or llv(x,n)
occurs in the current period, then the number returned will be 0.

Example: this is to show the period numbers since the last 20-day highest high and
20-day lowest low. In the chart you can see how the returning numbers are changing.

# william's R
hh : hhvbars(high, 20);
ll : llvbars(low, 20);

sumbars(x, a)

x:

an array of numbers,

a:

a single number.

Function sumbars(x,a) counts the period number
n in the past so that
sum(x, n) >= a.

Example: this example is another approach to measure the market tradability and liquidity.
The day_no is the number of trading days the market needs
to have a cumulative total as
capital.
Obviously,
the smaller day_no is, the more active is the market.

day_no : sumbars(vol, capital);

Example: the next example picks stocks (when dd is 1) based on
the believe that when price goes down and OBV moves up, a rally is
likely coming.

The function barscount(x) counts the period number
since the first period of x.

Example: in the formula below, no is the day number since the stock inception.

no : barscount(close);

Note that for the performance reason TSFS does not always load all the
historical price data when calculating barscount.
Internally, it loads historical data from the charts starting date two years ago.
As a consequence, the value of no
is precise only if the stock inception date is falling in the interval
starting from the charts starting date two years ago to the charts end date.

barssince(x)barslast(x)

x:

an array of numbers,

n:

period number, it has to be an integer.

Function barssince(x) counts the
period number since the first x != 0,
while function barslast(x) counts the
period number since the last x != 0.

Example: the formula below demonstrates the use of barssince
and barslast,
where a equals 1 only
in the last 10 days in the chart, the value of b
is climbing in the last 10 days. d
is the day number since the last occurrence of
close = hhv(close, 5).

Example: this example draws a happy face above the last 30-day highest high
and a sad face below the last 30-day lowest low in the chart.
In the example, islastbar
returns true if the current
period is the last period in the chart,
backset
returns 1s after last
hhv(h,30) and
llv(l,30)
occur. The results are stored in variables a1 and a2.
You can see how a1 and a2
are changing values in subcharts below the main chart.
The count function makes sure that
condition1 and condition2
are set to true only when a1 and a2
switch from 0 to 1.
drawicon plots icons
when conditions are true.

period number, it can be either an integer or an integer array,
n != 0.

Function filter(condition, n) returns the first true value of
condition and filter out all the m
consecutive true values of
condition within n periods, where
m < n.
If for the period t,
condition(t) != 0,
condition(t+1) != 0, .., condition(t+m) != 0
and y is the array returned by filter,
then y(t) = 1, y(t+1) = 0, .., y(t+m) = 0.

Example: in this example, condition is true if the price is closed up.
The variable result is set to 1 for the first true condition
and other consecutive true conditions are ignored.

Example: the next example highlights the 11-day highest high and the 11-day lowest low with
small blue and red balls, respectively. It then draws two horizontal lines as
the resistant line and the support line using the function
drawline.

Logical Functions

Logical functions are used to analyze the relationship between
different values and their relative positions in charts.
Except the if
function, logical functions return boolean values that are either true
(1) or false (0).

These three functions are provided for easy reference.
isup is equivalent to the comparison expression
close > open. It returns 1 when
the price is closed above the open price and returns 0 otherwise.
Likewisely,
isequal is equivalent to
close = open and
isdown is equivalent to
close < open.

Example: the following formula draws candlesticks using different colors depending on
whether or not the price is closed above, equal to or below the open price.

This function is used to check if the current period is the last period in the
chart. It returns 1 if yes, otherwise, it returns 0. For
example:

drawicon(islastbar, low, 1), align1;

isusmarketiscamarket

isusmarket and iscamarket
are provided to give the user a way to generate signals selectively.
isusmarket returns 1 if it is applied to stocks in US markets,
it returns 0 otherwise.
iscamarket returns 1 if it is applied to stocks in Canadian markets
and returns 0 otherwise.

isusmarket and iscamarket are
useful if you just
want your formulas to generate signals for
US or CA markets when scanning stocks.
The following example shows how you can use
isusmarket and iscamarket
in your formulas.

Function exist(condition, n) returns 1 if there exists at least one
period in the last n periods where
condition is true, i.e., condition != 0.
Otherwise, it returns 0.
Function every(condition, n) returns 1 only if for every period in the
last n periods, condition != 0.

Example: the following simple formula demonstrates the use of
exist and every.

a : exist(c < ref(c, 1), 3);
b : every(c < ref(c, 1), 3) * 2;

last(condition, n1, n2)

condition:

an array of numbers,

n1:

period number, it has to be a single integer number,

n2:

period number, it has to be a single integer with n2 <= n1.

Function last(condition, n1, n2) returns 1 if
condition is always true
from the previous n1-th period to
the previous n2-th period,
It returns 0 otherwise.

Example: this example checks if the market was up on the last 2 trading days.

last(isup, 3, 2);

longcross(a, b, n)

a:

a single number or an array of numbers,

b:

a single number or an array of numbers,

n:

period number, it has to be a single integer.

The function longcross(a,b,n) returns 1 if
in the current period, a > b
and in the previous n periods,
a < b. It returns 0 otherwise.
longcross(a,b,n) is equivalent to
the conjunction expression

cross(a, b) and last(a < b, n, 1)

Example: this example is to verify that longcross(a,b,n)
is equivalent to
"cross(a,b) and last(a<b,n,1)".
In the formula, ma5, ma10 are the 5-day and 10-day moving averages,
cross is the result of the conjunction expression,
long_cross is the return value of
longcross. long_cross is
multiplied by 2 so that it will not overlap cross in the chart.

Math Functions

TSFS does not provide a long list of math functions because
most complicated calculations are integrated into
different functions. This section covers some very
basic math functions you may need when writing your custom
formulas.

abs(x)

x:

a single number or an array of numbers.

Function abs(x) returns the absolute value of x.

max(a, b)min(a, b)

a:

a single number or an array of numbers,

b:

a single number or an array of numbers.

Function max(a,b) compares the value of a
and b and returns the one with greater value.
Similarly,
function min(a,b) compares the value of a
and b and returns the one with smaller value.

Functions max and min only take two
arguments. If you have more values to compare, use for example
max(max(a, b), c) to get the maximum value of
a,b,c
or min(min(a, b), c) to get the minimum value of
a,b,c.

Example: the indicator ATR (Average True Range) was created by J. Welles Wilder.
Its primary use is for determining the volatility of the market.
The idea is to replace the high-low trading range
for the given period, as it does not consider gaps and limit moves.
The true range in ATR takes the
previous close into account and is calculated in
the following way.

Example: DMI (Directional Movement Index) is another indicator
developed by J. Welles Wilder for identifying when a definable
trend is present in a stock.
The following formula is an implementation of DMI.

The function mod(x,divisor) returns the remainder of x
divided by divisor. Note that when calculating,
x and divisor are
first casted to integer numbers.
For example, if x is an array of (12.3, 0.6, -3.5), then
intpart(x,10) will return (2, 0, -3).

The function pow(x,n) returns the value of
x raised to the power of n.
For
example,

pow(c, 0.5);

log(x)ln(x)

x:

a single number or an array of numbers.

The function log(x) returns the common logarithm (10-based) of
x,
ln(x) returns the natural logarithm of x.
For
example,

log : log(c);
ln : ln(c)/2;

Statistic Functions

In technical analysis, sometimes we need static functions
to analyze the price movement in the past.
This section describes statistic functions provided by TSFS.

avedev(x, n)

x:

an array of numbers,

n:

period number

Function avedev(x,n)
calculate the average of the absolute deviations of
x in n periods.

Example: The Commodity Channel Index (CCI) uses avedev
to measure the position of price in relation to its moving average.
This can be used to highlight when the market is overbought/oversold or
to signal when a trend is weakening. The indicator is similar in concept
to Bollinger Bands but is presented as an indicator line rather than as
overbought/oversold levels.
CCI was developed by Donald Lambert.

The function slope(x,n) uses the least squares method to
find the best fitting line of x in n periods
and returns the slope of the line.

Example: the following formula displays the slope of the 20-day best fitting line of
close.
For a more meaningful example of slope, refer to the
discussion of TREND.

0, colorred;
slp : slope(close, 20);

relate(x, n)relate(x, y, n)

x:

an array of numbers,

y:

an array of numbers,

n:

period number

The function relate calculates the correlation coefficient.
The correlation coefficient is used to measure "goodness-of-fit" in pattern-fitting
procedures.
relate(x,n) checks the match of x
with ascending straight lines in n periods, while
relate(x,y,n) measures the match of x
with y in n periods.
The returning value of relate ranges
between 1 and -1, where a value of 1 indicates a perfect match,
i.e., the two patterns are identical. A value of -1
indicate that an exact match had been found, but that it is "upside-down".
Values near zero mean there is no match at all.

In practice it has been found that values of 0.8 or more correspond to
patterns in the data that are easily discerned as "good matches" by the human eye.

Example: an interesting use of relate is that you
can let TSFS find all the stocks
whose price trend closely matches that of the Nasdaq Composite Index
in the last 20 days. In the example,
ochl is a formula
predefined in TSFS, "^ixic$ochl.c" is the
close price of Nasdaq Composite Index.
If you save this formula in your account, TSFS will report
matching stocks to you by generating
match signals everyday!

Example: with little changes the above formula becomes even more interesting and
can be used to track down stocks who followed the nasdaq index trend closely until yesterday
(because of the use of ref(c,m) with m = 1).
By watching out the index move today,
you can "foresee" where your stocks are going to head to tomorrow.

Indicator Functions

To protect you from
writing some tedious but useful formulas you often need,
TSFS provides
a list of indicator functions that you can use directly in your formulas.

zig(x, n)zig(n)

x:

an array of numbers,

n:

a percentage number

The function zig(x,n) returns results representing zigzag lines which
reverse when the change of x is more than n%.
The function zig(n) is a short form of
zig(x,n) and uses high and low prices to return zigzag lines.
Function zig filters out random noise and daily price fluctuations
and can be used to identify waves for Elliott Wave counts.
Please note that zig uses future data to determine when to
reverse zigzag lines. Because of this, zig should not be used
in any formulas generating trading signals.

Example: this example shows the difference between
zig(x,n) and zig(n).
Notice that peaks and troughs of zig(3) appear
at high and low prices, while
zig(low, 3) always uses the low price.

z1 : zig(3), linethick2;
z2 : zig(low, 3);

Example: function zig(x,n) is flexible so that you
do not have to always apply it to price data. In the following formula,
it is applied to 5-day moving averages.

ma: ma(close, 5);
zig(ma, 2);

peak(x, n, m)peakbars(x, n, m)peak(n, m)peakbars(n, m)

x:

an array of numbers,

n:

a percentage number

m:

an integer number.td>

These four functions are all about peaks of zigzag lines and are thus
closely related to functions zig(x,n)
and zig(n).
The function peak(x,n,m)
returns the mth last peak value of zig(x,n),
while peakbars(x,n,m) returns the period number between the current
period and the mth last peak of zig(x,n).
peak(n,m) and peakbars(n,m) are short forms
of the above two functions and use high and low prices to
return the mth last peak value and
its period number of zig(n).

Example: this example includes two formulas. In the main chart,
zig(p) is plotted with the brown color.
peak(p,1) (the green line) changes its value
when a new peak is encountered.
peak(p,2) (the red line) follows
the green line because
peak(p,2) returns the second last
peak value of zig(p).
The period numbers are plotted by the second formula in the
subchart below. Colors in the subchart matches that in the main chart.
So you see how the period numbers are changing.

These four functions are the opposite of that of
peak and peakbars.
The function trough(x,n,m)
returns the mth last trough value of zig(x,n),
while troughbars(x,n,m) returns the period number between the current
period and the mth last trough of zig(x,n).
trough(n,m) and troughbars(n,m)
are short forms of the above two functions and use high and low prices to
return the mth last trough value and
its period number of zig(n).

Example: this example includes two formulas. In the main chart,
zig(p) is plotted with the brown color.
trough(p,1) (the green line) changes its value
when a new trough is encountered.
trough(p,2) (the red line) follows
the green line because
trough(p,2) returns the second last
trough value of zig(p).
The period numbers are plotted by the second formula in the
subchart below. Colors in the subchart matches that in the main chart.
So you see how the period numbers are changing.

These four functions are the combination of functions
peak, trough and peakbars, troughbars.
peaktrough(x,n,m) returns the mth
last peak/trough value of zig(x,n),
while peaktroughbars(x,n,m) returns
the period number between the current
period and the mth last peak/trough of
zig(x,n).
peaktrough(n,m) and peaktroughbars(n,m)
are short forms
of the above two functions and use high and low prices to
return the mth last peak/trough value and
its period number of zig(n).

Example: this shows both zig(p) and
peaktrough(p,1) in the main chart.
peaktrough(p,1) (the green line) changes its value
when a new peak or trough is encountered.

sar stands for parabolic stop-and-reverse.
It returns trailing stop positions that follow a prevailing trend.
sar was developed by J. Welles Wilder.
Basically, if the stock is trading below sar,
you should sell. If the stock price is above sar,
you should buy or stay long.

Function sar(n,s,m) uses
hhv(high,n) and llv(low,n)
in the computation. The initial and incremental step is s%,
the maximum step is m%. In practice,
s and m are often set to
2 and 20.

The calculation of functions cost and winner
are based on the cumulative total volume in the indicator of CYQ.
The function cost(n) returns the cost price under that
there are n% of the shares displayed in
the CYQ chart.
The function winner(x) returns numbers from 0-1
indicating the percentage of shares displayed in the
CYQ chart that are
held below the price x.

For example, if cost(50) returns the price
p, then 50% of total volume in
CYQ were accumulated under the price p,
and another 50% of the total volume were trading above p.
Obviously, winner(p) = 0.5.

Because CYQ is dependent on
capital,
functions cost and winner
are available only if
capital
is present for the given stock.

Example: Function cost is directly related to trading volumes.
Therefore, price changes with light volumes have little effect on
cost.
The following example shows cost with different
parameters in the main chart.

cost20 : cost(20);
cost50 : cost(50);
cost80 : cost(80);

Example: Crowded trading areas often serve as resistance or support.
When the price penetrates a crowded trading area, it is
expected to see a high volume. If the volume is light,
it is suspected that the stock is under control
of some big investors or market makers.
The formula below exams if the trading range penetrates
a 15% volume interval in the CYQ chart
with a trading volume relatively small. If this is
the case, it displays a small blue ball
on the top of the penetrating volume avol.

Drawing Functions

This set of functions are provided to draw calculation results in charts.
You can use them to draw lines, icons, candlesticks, text strings etc. in
different parts of charts based on your conditions. Together with
drawing descriptors that will be covered in the next section, you have
all the freedom to produce clear, informative and colorful charts for your
custom indicators.

drawicon(cond, pos, icon_idx)

cond:

a single number or an array of numbers,

pos:

a single number or an array of numbers,

icon_idx:

integer number from 0-12.

The function drawicon draws an icon specified
by icon_idx at the position indicated by
pos when the condition
cond holds, i.e., cond != 0.
The number of icon_idx has the following meaning:

0: happy face;

1: happy face;

2: sad face;

3: face;

4: blue up arrow;

5: red down arrow;

6: blue flag;

7: red flag;

8: blue up triangle:

9: red down triangle;

10: blue boll;

11: red boll;

12: green boll;

Example: this is to draw all the different icons
on trading days from 2005-10-11 to 2005-10-27
at the position 10% higher than the
high price.

Example: The next formula calls the predefined formula KDJ and marks buy signals with
happy faces and sell signals with sad faces. The descriptor
align1
indicates that icons are to be drawn
at the position a bit lower than the low
price.

The function drawline draws line segments starting at position
pos1 where the condition cond1 holds
and terminating at position pos2 where the condition
cond2 holds. The two positions have to be in
different periods. If expand != 0,
the function expands line segments from position pos2
to the chart board.

Example: the following formula draws an expanded trend line.
The line starts from the
position where the high price is the highest high in the last 80 days.
The starting position is marked with a small blue ball using the drawing function
drawicon(cond1, h, 10).
The line then goes through the second position where the high price is the highest high
in the last 40 days. The second position is marked with a small red ball using the function
drawicon(cond2, h, 10).

The function polyline draws connected line segments
using pos as vertices where the condition
cond holds.

Example: the formula in this example first identifies high prices that occur 2 days ago and are
the 5-day highest high. The identified high prices are marked
with small blue balls and then the function polyline
is called to draw line segments passing through these vertices.

Example: this example draws a set of different moving averages of ma(close,5).
Because the constant number 1 is used as the condition, the condition holds in
every period and
polyline
draws all the moving averages in all periods.

Example: this example demonstrates how to use polyline
to draw horizontal lines. It highlights the recent trading range
with two horizontal lines
using the last 40-day high
and 40-day low prices in the chart.
Because of the use of backset, the condition
count(a1, 2)>0 always holds since the last occurrence of
hhv(h,n) and so is the condition
count(a2, 2)>0 since the last occurrence of
llv(l,n).
Because ref(h,bars1) and ref(h,bars2)
will always return the same hhv(h,n) and llv(l,n),
respectively,
polyline displays two horizontal lines in the chart.

When the condition cond holds,
function stickline draws line sticks or rectangles
from position
pos1 to position pos2
depending on whether width = 0 or not.
If width = 1,
it draws rectangles with the same width as volume bars.
If width > 1,
the rectangle width will be that of candlesticks in the main chart.
When drawing rectangles, if empty = 0,
rectangles are filled with the color specified by color
descriptors (we will cover this topic in the next section),
or the default color will be used. If empty = 1,
rectangle are remained unfilled.
Function stickline can be used to produce impressive and colorful
charts.

Example: by using stickline, you can
redraw all the candlesticks in the main chart using different colors.
In the formula below,
if the price is closed above the previous close price, we use the blue color, otherwise, use
the green
color. The stick width is set to 2 when drawing from close to
open.
If is_up is true, draw sticks with empty=1,
otherwise, set empty=0.

When the condition cond holds, i.e.,
cond != 0,
function fillrgn(cond,pos1,pos2)
fills the region formed by positions pos1 and
pos2 with the default color if the color is not specified.
fillrgn(pos1,pos2) is a short form for
fillrgn(pos1>pos2, pos1,pos2), i.e.,
the region is filled only if pos1 > pos2.

Example: in this example, the region bounded by
ma(c,5) and
ma(c,10) is filled by the blue color when
ma(c,5) is above ma(c,10).
Otherwise, it is filled by the color ff99cc.
In the formula, transparency5
is a color blending
descriptor indicating that 50% of the background color is allowed to shine
through the drawing color.

The function vertline draws
vertical lines from the top to the bottom in charts
when the condition
cond holds, i.e.,
cond != 0.

Example: the formula in this example draws vertical lines in the main chart when it is
the first trading day in a new month.

a := month != ref(month, 1);
vertline(a), color999999;

Example: you can also use vertline to mark
certain conditions in the chart when debugging formulas.
The following formula marks candlesticks when
the price is closed up in 3 consecutive days and
the close price is above its 20-day moving averages.

Example: the formula below draws the string "Rally started?" slightly below the low price
when yesterday's low price is the
lowest low price in the last 40 days and the current close price is 10% higher than
the open price.

Drawing Descriptors

TSFS provides a few drawing descriptors to enhance the flexibility when
drawing charts and to improve the chart quality. All drawing descriptors
have to be used in conjunction either with expressions returning data,
or with specific drawing functions. The general
syntax of using drawing descriptors is

All the three descriptors are related to drawing sticks in charts.
The stick draws sticks from the 0-line;
colorstick draws sticks from the 0-line using two
different colors depending on if the value returned by its expression
is above or below the 0-line;
the linestick does the same thing as stick
does, plus it also draws line segments connecting all the values
returned by its expression in charts.

Example: this is to
demonstrate the use of stick
and colorstick in two separate subcharts.

The align descriptor takes an integer number
from 0-5 as part of its name to indicate the position alignment when
drawing icons using the function drawicon.
The number has the following meaning.

align0: draw at the desired position;

align1: draw slightly below the position;

align2: draw slightly above the position;

align3: draw at the center of the chart;

align4: draw at the top of the chart;

align5: draw at the bottom of the chart.

Example: the following formula draws different icons on the day 2005-10-17 at different positions.

cond := date=1051017; # if it is 2005-10-17
drawicon(cond, c, 10), align0; # draw a small blue ball at the close price position
drawicon(cond, c, 11), align1; # draw a small red ball below the close price
drawicon(cond, c, 12), align2; # draw a small green ball above the close price
drawicon(cond, c, 3), align3; # draw a face at the chart center
drawicon(cond, c, 4), align4; # draw an blue arrow at the chart top
drawicon(cond, c, 5), align5; # draw an red arrow at the chart bottom

linethick[0-7]

The linethick descriptor takes an integer number
from 0-7 as part of its name to specify the thickness of lines to be drawn
in charts. Note that when linethick0 is used,
the line thickness is supposed to be 0, i.e., no lines will be drawn.

The color descriptor specifies which color should be
used when drawing in charts.
The word color
has to be followed either by a predefined color name or 6 color hex code from
000000-ffffff. For example
color0000ff. The predefined color names are those
listed below.

The transparency descriptor takes an integer number
from 0-9 as part of its name to indicate the percentage of
how much the underlying color can be allowed to shine through
the drawing color (percentage of color transparency).
For example,
transparency9 means 90% color transparency,
transparency0 means 0% color transparency, i.e.,
no color transparency at all and the drawing color is used literally.
transparency is effective only when it is
applied to the function fillrgn.

Example: the following formula draws the BOLL (Bollinger Bands) indicator in
the chart. The region formed by
the boll upper line the boll lower line is filled with color 6464fa using 80%
color transparency.

Miscellaneous Functions

an array of numbers containing 1, -1, 0, where 1 stands for buy signals, -1 for sell signals and 0 for no trading activity.

sigperform uses x as
the price and emulates buy/sell activities according to the numbers in
signal. It returns gain/loss percentage numbers
as the performance of signal.

sigperform
begins calculation with holding cash and waiting for the first buy signal.
It uses all cash to buy when seeing a buy signal and sells all shares when meeting a
sell signal.
Therefor, sell signals before the first buy signal are ignored,
consecutive buy or sell signals are ignored except the first one.

Suppose x and signal are given as
(11, 12, 13, 14, 15, 16, 17),
(-1, -1, 1, 1, 0, -1, -1), respectively.
sigperform starts with holding 100 cash and
will ignore the first 2 sell signals as it has nothing to sell.
sigperform will use all cash to buy
100/13 = 7.6923 shares when it sees the first buy signal.
The second buy signal is ignored because there is no cash to buy anything.
The number 0 means no activity and will simply be skipped.
When the following sell signal is seen,
sigperform will sell all the 7.6923 shares and
hold 7.6923*16 = 123.0769 cash.
The next sell signal is ignored because there is nothing to sell.
Thus the market value of the initial 100 cash during the process will be
(100, 100, 100, 107.6923, 115.3846, 123.0769, 123.0769),
the signal performance in percentage number will be
(0, 0, 0, 7.6923, 15.3846, 23.0769, 23.0769).

Example: sigperform can be used to compare performance of different
indices and stocks. In the following formula, we compare IBM's performance with that of
S&P 500 Index. In the formula, sig is set to 1 for all the
periods on the chart. According to what we have explained above, we buy in the first
period and hold in the rest of entire time frame on the chart.
This is effectively the so-called
buy-and-hold strategy: buy once and hold forever. In the formula,
ochl is a predefined formula
in TSFS,
"^gspc$ochl.c" returns the close price of S&P 500 Index and
sp500 is the S&P 500 Index performance in the
time frame on the chart.

Example: Some investment advisors suggust a simple strategy for small investors:
buy when the price is closed above ma(close,10)
and sell when the price is
below ma(close,10).
This strategy works well when markets go straight up and down.
We can check how good this strategy really is by comparing its performance with that
of the buy-and-hold strategy(see the above example). We take IBM as
example. Since IBM is not as volatile as small-cap stocks, in the following
formula, instead of using ma(close,10),
we use ma(close,30).

TSFS has predefined a big number of formulas. Most of them are used
as public indicators and can be found in the expandable list on
the left side of the page
editing and testing formulas.
If you click on an indicator name in the list, you should see the description
and the implementation of that indicator on the right side.

Some of the predefined formulas are used to scan markets everyday after market close.
The formula code and their explanations are available on
Scan Descriptions.

TSFS has also implemented a few formulas that are
initially for the internal use.
In this chapter, we are going to go through these formulas. You may find
that some of them are interesting to you.
Examples of using these formulas can be found in
Scan Descriptions.
In addition,
this chapter will also discuss some special indicators in detail that are only
available on TecStock.

BASIC_COND

BASIC_COND serves as the
basic condition TecStock.com uses when scanning markets.
BASIC_COND helps filter out stocks
that either are closed below $0.30 or were trading with a
volume of less than 20,000 in the last 20 days.

Stocks at this level tend to produce a high degree of misleading signals,
which simply results in a waste of the traders time.

The parameter P is to select which price the
formula should use.
If P = 4, for instance, the formula will use
low.
The parameter N is the period number.
slope(price,n) returns the slope of
price in the last n periods,
slope(price,n)*n is an estimation of the
price change in the last n periods.
Thus, the variable slp is the estimated percentage change
of price from the closing price n
periods ago.
relate(price,n) evaluates how good
the price matches ascending straight lines.
Recall that if price is in downtrend,
the return value of relate(price,n) will
be less than 0.
Parameter S is the given percentage number,
CORREL is the input correlation coefficient.

If you call the formula using "trend.up"(4, 20, 10, 0.8),
the formula set variable
up to 1 (true) if the low
price was moving straight up (rlt > 0.8)
and has increased more than 10% (approximately) compared
to the close price 20 periods ago.
If you call the formula using "trend.down"(3, 20, 10, 0.8),
it sets the variable down to 1 (true)
if the high
price moved straight down (rlt < -0.8) and
lost more than 10% (approximately) compared
to the close price 20 periods ago.

CYQ

The
CYQ indicator
is one of the tools only available on
TecStock. It provides many raw market
information that are important but have been so far ignored
by a lot of investors.

CYQ
is a dynamic version of the PBV indicator
(Price By Volume). PBV combines both price and
volume into one tool and plots horizontal volume
bars onto the chart at price intervals. The length
of each bar is determined by the cumulative total
of all volume for which the closing price fell
within the price interval.

In PBV charts, the volume bar length will never
be decreased even if the stock has been trading above
or below the bar for long time. From this point of
view, PBV is a static indicator. CYQ, on the other hand,
takes account of the fact that the cumulative volume in
each price interval will be consumed when the stock is
not trading in their price ranges. CYQ uses an emulation
algorithm to estimate the volume to be taken off in each
volume bar. The estimation may not always be precise. With
the time, however, it gives a relatively accurate picture
of the cost structure of the stock. CYQ is thus also
referred to as the dynamic cost chart.

CYQ is a very useful indicator. Like PBV,
CYQ can be
used to identify crowded trading areas and the related
support, resistance levels. In addition to this, big
institutional positions can not hide in the CYQ chart.
CYQ can be used to estimate average holding cost of big
investors' positions. The
animated CYQ
can even show how the cost structure is changing.

What follows are the CYQ
label you often see in the CYQ chart and
the explanation of the label.

CYQ(60) indicates that the CYQ chart
is consisting of 60 horizontal volume bars.
The more volume bars we use, the more precise and slower the chart will be.
We have tested many other numbers and believe that 60 is suitable for most of
users. This number is currently not configurable.

(5.5B) is the
capital,
the number of shares held by the public and available for trading
(often referred to as shares float).
This is different from the number of shares outstanding.
5.5B stands for 5.5 billion shares.

CYQ is calculated based on capital.
Unfortunately,
this number is not always available for all stocks. This is also why for
some stocks there is no CYQ chart.

Ideally, CYQ would be calculated from the stock inception date.
This is, however, sometimes neither possible nor necessary.
Whenever possible, Tecstock.com calculates CYQ at least
from the date earlier than the current date 2 years ago.

93.06% is the percentage of the cumulative total
from capital, i.e., the total cumulative volume in
the CYQ chart is 5.5B * 93.06% = 5.12B.
For active stocks with small
capitals this number is often 100%.

Cost(50):21.01 shows that
cost(50) = 21.01.
It is useful to know the price of cost(50)
as it can help figure out approximately how
expansive or cheap the current price is.

Winner:87.2% means that
87.2% of the volume in the CYQ chart were
trading under the last close price displayed in the chart.
In other words, owners of 87.2% of the shares displayed in the
CYQ chart are winners.

CYCCYC_INFINITYCYC_N

You have seen in the previous chapter
the concept of holding cost cyc
when we were discussing function
dma. Because of its usefulness,
TSFS has predefined a formula CYC.
CYC is consisting of two intermediate formulas
CYC_INFINITY and
CYC_N that are explained separately below.

Formula CYC_INFINITY returns the
same overall holding cost
cyc as we discussed before
with the exceptions that it uses
(open+close)/2 instead of close
and returns an empty result
if capital is not available.
The code of the formula is listed below. Notice that
the function cyc(...) used inside the formula
is an internal function that you should not use in your formulas.

cyc: cyc((open+close)/2, 0);

In contrast to the overall holding cost calculated by CYC_INFINITY,
formula CYC_N considers only
n-period data and returns
the average holding cost in the past
n-period.
CYC_N takes one parameter N and uses
(open+close)/2 as well.
If capital is not available,
it makes use of sum(vol,n) and
returns an approximate holding cost that is
accurate as long as the volume is not volatile.

The formula CYC itself is simply a combination of
CYC_N and CYC_INFINITY.
It takes 3 parameters and returns by default the
5-day, 10-day, 30-day average holding costs and the overall average holding cost
when used in daily charts. Here is an
example using CYC.

A direct use of CYC is to check whether
stocks are under bullish condition. If the stock is trading
above cyc,
it indicates that the price is higher than the overall average holding cost,
most investors are winning and the stock is bullish;
if the price is moving below cyc,
it implies that most investors bought the stock at more expansive prices
and are losing money, the stock is bearish.

You can do the same analysis in terms of n-period.
If, for example, the stock is trading above cyc3,
then by default parameters, most investors bought the stock in the last 30 days are winning;
if the price is below cyc3, then most investors bought the stock
in the last 30 days are losing.

If you are a cautious investor and are not willing to take too much risk, you can
choose stocks trading
above the overall average holding cost cyc.
If you are a bit aggressive and are
seeking for highly profitable opportunities, you can pick stocks
that are moving below cyc.

CYC is a lagging indicator as long as the trading volume is light.
Daily price fluctuations with light volumes have little effect on CYC.
Consequently, the relative position of different holding costs in CYC
is stable. This is very different from moving averages, where different moving averages
are often crossing up and down when the price movement is volatile. You can see this by
comparing two charts here with
one
have different average holding costs and
the other
different moving averages.

CYS

The formula CYS calculates the
bias of the n-period average holding cost. The only parameter the
formula takes is N, the period number. By default, N
is 10. Here is an
example
using CYS.

CYS is an indicator for checking the oversold condition.
Unlike other commonly used oversold indicators that check if the price has fallen sharply
to a certain degree over a brief period of time,
CYS is based on the holding cost and takes trading volume
into account. Basically, CYS considers a stock oversold
if its price rapidly declines on light volume. The condition of light volume
is essential as it indicates that only a small portion of people are in panic selling.
The most investors still
believe the value of the stock and are holding.
This implies that the stock is undervalued (at least temporarily)
and may represent a good buying opportunity for investors.

For big cap stocks, daily price changes are often limited to 2%, the
CYS value are usually ranging from -5 to 5.
If you bought a big cap stock when its 5-day CYS was -10,
it means that sellers sold their shares to you with an average loss of 10%
in the past 5 days. This is a big loss and the stock may be deemed to be oversold (recall
only a minority of investors are in panic selling). The following formula
can be used to signal such buying opportunities.