Learn technical analysis by building my trading bot (part 1)
I bought my first Bitcoin two years ago when it was around $3000 and has keep trading everyday since then. I love trading, it’s exciting to wake up in the morning and check my balance.
Everything is not going well, i burned out my account several times and never learned a thing. I’m still having fun while trading but it’s like an expensive hobby but not a business…
It’s time to get serious.
What is my plan?
As a technical person, i focus on technical analysis first (they’re both technical!). Last few weeks, i’ve read a lot of articles on that topic and joined any course i found, there’s no end of them. They give so many concepts, patterns that i can’t memorize them all. And even if i can, i don’t know how to use them. I definitely need a better way to learn.
Eventually i decided to learn it the programmer’s way: Build a trading bot from scratch. I hope it could helps me gain a better understanding of technical analysis. Here is the plan
I’m going to start with some basic building blocks. They’re not really interesting but i will need them to build other things.
Next, i will implement some well-known indicators which i saw in a lot of trading strategy.
Then figure out some way to determine support and resistance and trend line. I’m still confused about them so it may take some time.
When everything is ready, i will code my first strategy and, of course, backtest it.
Finally, connect broker/exchange API and put some money to work.
First thing first…
What is Technical Analysis?
This is the definition i got from investopedia
Technical analysis is a trading discipline employed to evaluate investments and identify trading opportunities by analyzing statistical trends gathered from trading activity, such as price movement and volume.
In short, we use past price action to predict price movement. How to do that is coming later. Let’s start by defining data structures to represent these historic data.
Data Point
The image above is an example of candletick chart, which is commonly used in technical analysis. A candle has 4 data:
Open – The opening price
High – The highest price
Low – The lowest price
Close – The closing price
There’s also volume data at the bottom of the chart. These values are aggregated during a single time period and identified by start timestamp. We will keep them in DataPoint
struct:
type DataPoint struct {
StartTime time.Time
OpenPrice num.Decimal
ClosePrice num.Decimal
HighPrice num.Decimal
LowPrice num.Decimal
Volume num.Decimal
}
with num.Decimal
is the interface:
type Decimal interface {
Add(addend Decimal) Decimal
Sub(subtrahend Decimal) Decimal
Mul(factor Decimal) Decimal
Div(denominator Decimal) Decimal
Compare(other Decimal) int
Float() float64
fmt.Stringer
}
We could implement num.Decimal
using float64
if we need high performance. Otherwise, if we need decimal with arbitrary precision, we could use math/big.Float
.
Below is the simple implement, which is a thin wrapper around float64
. For precision decimal implement and unit tests, go here
type simpleDecimal struct {
fl float64
}
func (*simpleDecimal) cast(d Decimal) *simpleDecimal {
if sd, ok := d.(*simpleDecimal); ok {
return sd
}
panic("incompatible decimal implement")
}
func (d *simpleDecimal) Add(addend Decimal) Decimal {
return &simpleDecimal{d.fl + d.cast(addend).fl}
}
// Sub, Mul, Div are similar ...
func (d *simpleDecimal) Compare(other Decimal) int {
o := d.cast(other).fl
if d.fl > o {
return 1
} else if d.fl < o {
return -1
}
return 0
}
func (d *simpleDecimal) Float() float64 {
return d.fl
}
func (d *simpleDecimal) String() string {
return fmt.Sprintf("%f", d.fl)
}
Time Series
Usually, we need a series of data points to analysis. These data points are taken at a same period described as chart time frame.
Time frame is an important variable we must consider when studying chart. It ranges from minute to daily or weekly, depend on the trader’s personal trading style.
Day traders could use 5-minute, 15-minute or 60-minute charts.
Swing traders could use 1-hour charts and daily charts.
Position traders could use daily charts, monthly charts or weekly charts.
Our TimeSeries
struct will look like this:
type TimeSeries struct {
candles []*DataPoint
timeframe time.Duration
}
func NewTimeSeries(timeframe time.Duration) (t *TimeSeries) {
return &TimeSeries{[]*DataPoint{}, timeframe}
}
We use Append
to add a DataPoint at the end of the series and At
to get a DataPoint
by index. Append
method makes sure the series is in time order.
func (ts *TimeSeries) Append(point *DataPoint) bool {
if point == nil {
panic("candle cannot be nil")
}
if len(ts.data) == 0 || point.StartTime.After(ts.data[len(ts.data)-1].StartTime) {
ts.data = append(ts.data, point)
return true
}
return false
}
func (ts *TimeSeries) At(index int) (*DataPoint, error) {
if index < 0 || index >= len(ts.data) {
return nil, ErrIndexOutOfRange
}
return ts.data[index], nil
}
That’s it for today.
In the next few parts of the series, we will use our DataPoints
and TimeSeries
to calculate some indicators.