Hello, Price
The problem
Bitcoin is trading at 67,410.50. You want the computer to say so.
That sounds too simple to be worth a chapter. It isn't, because between you typing that number and the machine printing it, four things happen that you'll rely on for the rest of the book.
Your first program
Make a folder, put this in a file called main.go:
package main
import "fmt"
func main() {
price := 67410.50
fmt.Println("The price is", price)
}Run it:
go run main.goYou should see:
The price is 67410.5If you got an error, read it. Go's error messages are unusually good - it will tell you the file, the line, and usually exactly what's wrong. Getting comfortable reading errors now, while they're trivial, is worth more than getting this program right first time.
What just happened
Four things, each worth knowing.
`package main` - Go organises code into packages. main is special: it means "this is a program you can run," not a library for other code to use. Every runnable Go program has exactly one.
`import "fmt"` - you're borrowing code someone else wrote. fmt is short for "format," and it handles printing. Go ships with a large standard library, and unlike most languages, that library is usually enough.
`func main()` - a function named main. When you run a Go program, the machine looks for this function and starts there. Chapter 5 is all about functions; for now, think of it as "the place the program begins."
`price := 67410.50` - this is the interesting one.
Boxes with labels
price := 67410.50 means: find a bit of memory, put the number 67410.50 in it, and let me call it `price` from now on.
That's a variable. A labelled box holding a value.
memory
┌──────────────┐
│ 67410.50 │ ← the value
└──────────────┘
↑
price ← the label you use to find itYou can put something else in the box later:
price := 67410.50
price = 67411.00 // note: = not :=
fmt.Println(price) // 67411:= means create this box and put something in it. = means the box already exists, replace what's inside. Using := twice on the same name is an error, and using = before the box exists is also an error. Go is picky about this on purpose: it means a typo in a variable name gets caught immediately rather than silently creating a second box.
Types: what kind of thing is in the box
Here's where Go differs from Python, and where it starts earning its keep.
Every box has a type - a declaration of what kind of value lives in it. Once set, it can't change.
price := 67410.50 // float64 - a number with a decimal point
volume := 1420 // int - a whole number
symbol := "BTCUSD" // string - text
isOpen := true // bool - true or falseYou never wrote the types. Go worked them out from the values - that's called type inference, and it's why Go code doesn't look as cluttered as you might expect from a typed language. You can write them explicitly when you want to be clear or when inference would guess wrong:
var price float64 = 67410.50
var volume int = 1420Now try breaking it:
price := 67410.50
price = "expensive" // error!cannot use "expensive" (untyped string constant) as float64 value
in assignmentThe program refuses to compile. Not "crashes when it runs" - refuses to become a program at all.
Why this is a feature, not an annoyance
This is the single biggest difference from Python, and it's worth understanding rather than tolerating.
In Python, putting a string where a number belongs is fine right up until you do arithmetic on it, which might be in a function three files away, running at 3 a.m., holding a real position. The failure happens far from the mistake.
In Go, the failure happens at the mistake, before the program exists. A whole category of bug - the kind where something is quietly the wrong kind of thing - simply cannot survive to runtime.
The cost is that you have to be explicit, and occasionally that's tedious. The benefit is that when a Go program compiles, a surprisingly large class of stupidity has already been ruled out. For code that moves money, that trade is a bargain.
Doing arithmetic
package main
import "fmt"
func main() {
entry := 67410.50
exit := 68200.00
quantity := 0.5
profit := (exit - entry) * quantity
fmt.Println("Entry: ", entry)
fmt.Println("Exit: ", exit)
fmt.Println("Quantity:", quantity)
fmt.Println("Profit: ", profit)
}The operators are what you'd expect: + - * /, plus % for remainder. Parentheses group things, and multiplication binds tighter than addition, exactly as in school arithmetic.
One trap, and it's a good one to hit early:
a := 7
b := 2
fmt.Println(a / b) // 3, not 3.5Both are int, so Go does integer division and throws away the remainder. This is not a bug, it's the defined behaviour of dividing two integers, and it will bite you the first time you compute an average price from integer inputs. To get 3.5, at least one side has to be a float:
fmt.Println(float64(a) / float64(b)) // 3.5float64(a) is a conversion: make a new float64 with the same value. Go never converts between number types automatically. Every conversion is written down, which is verbose and means you always know when one happened.
Printing properly
fmt.Println is fine for scribbling. fmt.Printf gives you control:
price := 67410.5
qty := 0.5
fmt.Printf("Price: %.2f\n", price) // Price: 67410.50
fmt.Printf("Buy %.4f BTC at %.2f\n", qty, price)
fmt.Printf("Symbol: %s, open: %t\n", "BTCUSD", true)The % bits are placeholders, filled in order by the arguments that follow:
| Verb | Meaning |
|---|---|
%d | integer |
%f | float - %.2f means two decimal places |
%s | string |
%t | boolean |
%v | anything, Go's default formatting |
%T | print the type rather than the value |
\n means newline. Println adds one for you; Printf doesn't, so you write it.
%T is worth remembering - when you're confused about what something is, fmt.Printf("%T\n", x) answers it immediately.
Exercises
1.1 Write a program that stores your entry price, exit price, and quantity for a trade, then prints the profit formatted to two decimal places.
1.2 Add the trade's return as a percentage: (exit - entry) / entry * 100. Print it to two decimals with a % sign after it. (Careful: % is special in Printf. Find out how to print a literal one.)
1.3 Predict the output of each line before running it:
fmt.Println(10 / 3)
fmt.Println(10.0 / 3)
fmt.Println(10 % 3)
fmt.Printf("%T\n", 10)
fmt.Printf("%T\n", 10.0)1.4 This program doesn't compile. Fix it without changing the values:
package main
import "fmt"
func main() {
shares := 100
price := 45.30
total := shares * price
fmt.Println(total)
}1.5 A round trip costs 0.1% commission on entry and 0.1% on exit. Extend 1.1 to subtract both commissions from the profit and print the net figure. Commission on each side is price * quantity * 0.001.
Solutions
1.1
package main
import "fmt"
func main() {
entry := 67410.50
exit := 68200.00
quantity := 0.5
profit := (exit - entry) * quantity
fmt.Printf("Profit: %.2f\n", profit)
}1.2 Use %% to print a literal percent sign:
returnPct := (exit - entry) / entry * 100
fmt.Printf("Return: %.2f%%\n", returnPct)1.3 3 (integer division) - 3.3333333333333335 (one side is float, so float division) - 1 (remainder) - int - float64.
1.4 shares is an int and price is a float64; Go won't mix them. Convert:
total := float64(shares) * price1.5
commission := (entry*quantity + exit*quantity) * 0.001
net := (exit-entry)*quantity - commission
fmt.Printf("Gross: %.2f Commission: %.2f Net: %.2f\n",
(exit-entry)*quantity, commission, net)