Skip to content

Commit 113a975

Browse files
committed
Initialize Bitcoin Price Tracker project with core functionality
- Added .gitignore to exclude build artifacts and environment files. - Created go.mod for module management. - Implemented main application logic in main.go for fetching and displaying Bitcoin prices. - Developed tracker package with functionality for price tracking, including API interaction and data models. - Added README.md with project objectives, features, and implementation plan. - Set up basic error handling and graceful shutdown for the application.
0 parents  commit 113a975

File tree

9 files changed

+339
-0
lines changed

9 files changed

+339
-0
lines changed

.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# If you prefer the allow list template instead of the deny list, see community template:
2+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3+
#
4+
# Binaries for programs and plugins
5+
*.exe
6+
*.exe~
7+
*.dll
8+
*.so
9+
*.dylib
10+
11+
# Test binary, built with `go test -c`
12+
*.test
13+
14+
# Output of the go coverage tool, specifically when used with LiteIDE
15+
*.out
16+
17+
# Dependency directories (remove the comment below to include it)
18+
# vendor/
19+
20+
# Go workspace file
21+
go.work
22+
go.work.sum
23+
24+
# env file
25+
.env

README.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
## **Project Title**: Bitcoin Price Tracker
2+
3+
### **Objective**
4+
Develop a Go-based command-line application (CLI) that allows users to:
5+
1. Fetch and display real-time Bitcoin prices in multiple currencies.
6+
2. Periodically update prices and notify users of significant changes.
7+
3. Log historical prices to a local file for review.
8+
9+
---
10+
11+
### **Features**
12+
1. **Fetch Current Prices**:
13+
- Retrieve real-time Bitcoin prices in user-specified currencies using a public API.
14+
15+
2. **Periodic Updates**:
16+
- Continuously fetch and update Bitcoin prices at configurable intervals using goroutines.
17+
18+
3. **Price Alerts**:
19+
- Notify the user when Bitcoin prices cross specified thresholds.
20+
21+
4. **Historical Logging**:
22+
- Log price data to a local file with timestamps for future analysis.
23+
24+
---
25+
26+
### **Go Concepts Incorporated**
27+
1. **Imports/Packages**:
28+
- Utilize standard libraries (`net/http`, `encoding/json`, `os`, `time`) and third-party packages as needed (e.g., `github.com/joho/godotenv` for managing API keys).
29+
30+
2. **Structs/Methods**:
31+
- Create structs for price data and configuration settings.
32+
- Define methods to fetch prices, format output, and handle updates.
33+
34+
3. **Error Handling**:
35+
- Gracefully manage HTTP request errors, JSON parsing issues, and file I/O problems.
36+
- Display meaningful messages to users for troubleshooting.
37+
38+
4. **Go Routines**:
39+
- Fetch Bitcoin prices concurrently without blocking user inputs.
40+
- Allow for background tasks like logging and threshold monitoring.
41+
42+
5. **Channels**:
43+
- Use channels for inter-goroutine communication, enabling real-time price updates and alerts.
44+
45+
---
46+
47+
### **Advanced Go Concepts (Optional)**
48+
1. **cgo**:
49+
- Add optional high-performance computation for price trend analysis.
50+
2. **Generics**:
51+
- Implement generic methods for handling API responses or data transformations.
52+
3. **Iterators**:
53+
- Use iterators for managing and displaying logged price history.
54+
55+
---
56+
57+
### **Project Structure**
58+
```plaintext
59+
bitcoin-tracker/
60+
├── main.go # Entry point of the application
61+
├── tracker/
62+
│ ├── tracker.go # Logic for Bitcoin price tracking
63+
│ ├── models.go # Structs for price data and alerts
64+
│ ├── api.go # API interaction logic
65+
├── utils/
66+
│ ├── error.go # Error handling utilities
67+
│ ├── config.go # Environment variable handling
68+
├── .env # API keys and config
69+
└── go.mod # Go module definition
70+
```
71+
72+
---
73+
74+
### **Implementation Plan**
75+
76+
#### **1. Core Features Implementation**
77+
1. Fetch real-time Bitcoin prices using a public API (e.g., CoinGecko or CoinMarketCap).
78+
2. Log historical data in a human-readable format.
79+
80+
#### **2. Application Flow**
81+
1. **User Input**:
82+
- Select currency for price tracking.
83+
- Set optional price thresholds for alerts.
84+
2. **Concurrent Operations**:
85+
- Run real-time price updates in a background goroutine.
86+
3. **Display Results**:
87+
- Continuously update the terminal with the latest price and any alerts.
88+
89+
#### **3. Incorporate Go Concepts**
90+
1. **Imports/Packages**:
91+
- Use Go modules for managing dependencies and clean code organization.
92+
2. **Structs/Methods**:
93+
- Define reusable structs for price data and configuration.
94+
3. **Error Handling**:
95+
- Implement proper error handling for all operations.
96+
4. **Go Routines**:
97+
- Fetch price data and monitor thresholds concurrently.
98+
5. **Channels**:
99+
- Use channels to coordinate data fetching and alert notifications.
100+
101+
---
102+
103+
### **Stretch Goals**
104+
- Add support for multiple cryptocurrencies (e.g., Ethereum, Litecoin).
105+
- Implement a simple web server to display real-time prices using `net/http`.
106+
- Use a visualization library to generate charts of historical price trends.
107+
108+
---
109+
110+
### **Deliverables**
111+
1. A functional CLI application for Bitcoin price tracking.
112+
2. Code that demonstrates mastery of Go basics:
113+
- Imports/Packages
114+
- Structs/Methods
115+
- Error Handling
116+
- Goroutines and Channels
117+
3. Documentation for setup, usage, and an explanation of the Go concepts used.

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module bitcoin-tracker
2+
3+
go 1.23.3

main.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package main
2+
3+
import (
4+
"bitcoin-tracker/tracker"
5+
"fmt"
6+
"log"
7+
"os"
8+
"os/signal"
9+
"strings"
10+
"syscall"
11+
"time"
12+
)
13+
14+
func main() {
15+
fmt.Println("🪙 Bitcoin Price Tracker")
16+
fmt.Println("----------------------")
17+
fmt.Print("Enter currency code (e.g., usd, eur, gbp): ")
18+
19+
var currency string
20+
fmt.Scanln(&currency)
21+
22+
currency = strings.ToLower(strings.TrimSpace(currency))
23+
if currency == "" {
24+
currency = "usd" // Default to USD if no input
25+
}
26+
27+
config := tracker.Config{
28+
UpdateInterval: 30 * time.Second,
29+
Currency: currency,
30+
}
31+
32+
t := tracker.NewTracker(config)
33+
t.Start()
34+
35+
// Handle graceful shutdown
36+
sigChan := make(chan os.Signal, 1)
37+
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
38+
39+
// Main loop
40+
for {
41+
select {
42+
case price := <-t.GetPriceChan():
43+
fmt.Printf("Bitcoin price: %.2f %s at %s\n", price.Price, price.Currency, price.Timestamp.Format(time.RFC3339))
44+
45+
case err := <-t.GetErrorChan():
46+
log.Printf("Error: %v\n", err)
47+
48+
case <-sigChan:
49+
fmt.Println("\nShutting down...")
50+
t.Stop()
51+
os.Exit(0)
52+
}
53+
}
54+
}

tracker/api.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package tracker
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
"time"
8+
)
9+
10+
const COINGECKO_API_URL = "https://api.coingecko.com/api/v3"
11+
12+
type CoinGeckoResponse struct {
13+
Bitcoin map[string]float64 `json:"bitcoin"`
14+
}
15+
16+
func FetchBitcoinPrice(currency string) (*PriceData, error) {
17+
url := fmt.Sprintf("%s/simple/price?ids=bitcoin&vs_currencies=%s", COINGECKO_API_URL, currency)
18+
19+
resp, err := http.Get(url)
20+
if err != nil {
21+
return nil, fmt.Errorf("failed to fetch price: %v", err)
22+
}
23+
defer resp.Body.Close()
24+
25+
var data CoinGeckoResponse
26+
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
27+
return nil, fmt.Errorf("failed to decode response: %v", err)
28+
}
29+
30+
price, ok := data.Bitcoin[currency]
31+
if !ok {
32+
return nil, fmt.Errorf("price not found for currency: %s", currency)
33+
}
34+
35+
return &PriceData{
36+
Price: price,
37+
Currency: currency,
38+
Timestamp: time.Now(),
39+
}, nil
40+
}

tracker/models.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package tracker
2+
3+
import "time"
4+
5+
// PriceData represents the Bitcoin price information
6+
type PriceData struct {
7+
Price float64
8+
Currency string
9+
Timestamp time.Time
10+
}
11+
12+
// Config represents the application configuration
13+
type Config struct {
14+
UpdateInterval time.Duration
15+
Currency string
16+
AlertThreshold float64
17+
APIKey string
18+
}
19+
20+
// Alert represents a price alert configuration
21+
type Alert struct {
22+
Type string // "above" or "below"
23+
Threshold float64
24+
Triggered bool
25+
}

tracker/tracker.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package tracker
2+
3+
import (
4+
"fmt"
5+
"time"
6+
)
7+
8+
type Tracker struct {
9+
config Config
10+
alerts []Alert
11+
priceChan chan PriceData
12+
errorChan chan error
13+
stopChan chan struct{}
14+
}
15+
16+
func NewTracker(config Config) *Tracker {
17+
return &Tracker{
18+
config: config,
19+
alerts: make([]Alert, 0),
20+
priceChan: make(chan PriceData),
21+
errorChan: make(chan error),
22+
stopChan: make(chan struct{}),
23+
}
24+
}
25+
26+
func (t *Tracker) Start() {
27+
go t.trackPrices()
28+
}
29+
30+
func (t *Tracker) Stop() {
31+
close(t.stopChan)
32+
}
33+
34+
func (t *Tracker) trackPrices() {
35+
ticker := time.NewTicker(t.config.UpdateInterval)
36+
defer ticker.Stop()
37+
38+
fmt.Printf("🔄 Fetching Bitcoin price every %v...\n", t.config.UpdateInterval)
39+
fmt.Println("Press Ctrl+C to exit")
40+
41+
// Fetch immediately first
42+
fmt.Print("Fetching latest price... ")
43+
if price, err := FetchBitcoinPrice(t.config.Currency); err != nil {
44+
fmt.Println("❌")
45+
t.errorChan <- err
46+
} else {
47+
fmt.Println("✅")
48+
t.priceChan <- *price
49+
}
50+
51+
for {
52+
select {
53+
case <-ticker.C:
54+
fmt.Print("Fetching latest price... ")
55+
price, err := FetchBitcoinPrice(t.config.Currency)
56+
if err != nil {
57+
fmt.Println("❌")
58+
t.errorChan <- err
59+
continue
60+
}
61+
fmt.Println("✅")
62+
t.priceChan <- *price
63+
case <-t.stopChan:
64+
return
65+
}
66+
}
67+
}
68+
69+
func (t *Tracker) GetPriceChan() <-chan PriceData {
70+
return t.priceChan
71+
}
72+
73+
func (t *Tracker) GetErrorChan() <-chan error {
74+
return t.errorChan
75+
}

utils/config.go

Whitespace-only changes.

utils/error.go

Whitespace-only changes.

0 commit comments

Comments
 (0)