Init
This commit is contained in:
commit
67a5d3abc0
0
.gitignore
vendored
Normal file
0
.gitignore
vendored
Normal file
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017, Kevin Cotugno
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
83
cmd/interval/main.go
Normal file
83
cmd/interval/main.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
"encoding/csv"
|
||||||
|
"log"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"tacitus/postgres"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
client := postgres.NewClient()
|
||||||
|
client.Name = "gdax"
|
||||||
|
client.User = "gdax"
|
||||||
|
|
||||||
|
err := client.Open()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var start time.Time
|
||||||
|
var end time.Time
|
||||||
|
var done bool
|
||||||
|
file, err := os.Create("btcusd_30_sec.csv")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
csv := csv.NewWriter(file)
|
||||||
|
|
||||||
|
first, _ := client.TradeService().Trade(1)
|
||||||
|
|
||||||
|
start = first.Timestamp.Truncate(30 * time.Second)
|
||||||
|
end = start.Add(30 * time.Second)
|
||||||
|
|
||||||
|
err = csv.Write([]string{"timestamp", "price", "volume", "transactions"})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastPrice string
|
||||||
|
for !done {
|
||||||
|
trades, _ := client.TradeService().TradesInDateRange("BTCUSD", start, end)
|
||||||
|
if len(trades) > 0 {
|
||||||
|
var volume decimal.Decimal
|
||||||
|
var transactions int
|
||||||
|
|
||||||
|
for _, t := range trades {
|
||||||
|
volume = volume.Add(t.Size)
|
||||||
|
transactions += 1
|
||||||
|
lastPrice = t.Price.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []string{end.String(), lastPrice, volume.String(), strconv.Itoa(transactions)}
|
||||||
|
if err = csv.Write(data); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(end)
|
||||||
|
} else {
|
||||||
|
data := []string{end.String(), lastPrice, "0", "0"}
|
||||||
|
if err = csv.Write(data); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = end
|
||||||
|
end = end.Add(30 * time.Second)
|
||||||
|
|
||||||
|
if start.After(time.Date(2017, 8, 30, 16, 13, 50, 0, time.UTC)) {
|
||||||
|
done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
csv.Flush()
|
||||||
|
file.Close()
|
||||||
|
}
|
15
config/postgres.sql
Normal file
15
config/postgres.sql
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
CREATE TABLE trades (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
trade_id integer NOT NULL,
|
||||||
|
product CHAR(6) NOT NULL,
|
||||||
|
price NUMERIC(1000, 8) NOT NULL,
|
||||||
|
size NUMERIC(1000, 8) NOT NULL,
|
||||||
|
buy BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
sell BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
timestamp TIMESTAMPTZ NOT NULL);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX trade_id_product_INDEX ON trades (
|
||||||
|
trade_id,
|
||||||
|
product);
|
||||||
|
|
||||||
|
CREATE INDEX timestamp_index ON trades (timestamp);
|
53
postgres/client.go
Normal file
53
postgres/client.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"tacitus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const connStr = `user={{.User}} dbname={{.Name}} sslmode={{.SslMode}}`
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
Name string
|
||||||
|
User string
|
||||||
|
SslMode string
|
||||||
|
|
||||||
|
tradeService TradeService
|
||||||
|
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient() *Client {
|
||||||
|
c := Client{}
|
||||||
|
c.tradeService.client = &c
|
||||||
|
c.SslMode = "disable"
|
||||||
|
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Open() error {
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
tmpl, _ := template.New("conn").Parse(connStr)
|
||||||
|
tmpl.Execute(&buf, c)
|
||||||
|
|
||||||
|
db, err := sql.Open("postgres", buf.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = db.Ping(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.db = db
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) TradeService() tacitus.TradeService {
|
||||||
|
return &c.tradeService
|
||||||
|
}
|
96
postgres/trade_service.go
Normal file
96
postgres/trade_service.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"tacitus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
trade_insert = `INSERT INTO trades (trade_id, product, price, size, buy, ` +
|
||||||
|
`sell, timestamp) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id;`
|
||||||
|
trade_find = `SELECT id, trade_id, product, price, size, buy, sell, ` +
|
||||||
|
`timestamp FROM trades WHERE id = $1;`
|
||||||
|
trade_find_trade_id = `SELECT id, trade_id, product, price, size, buy, ` +
|
||||||
|
`sell, timestamp FROM trades WHERE trade_id = $1 AND product = $2;`
|
||||||
|
trade_delete = `DELETE FROM trades WHERE id = $1;`
|
||||||
|
trades_in_date_range = `SELECT id, trade_id, product, price, size, buy, ` +
|
||||||
|
`sell, timestamp FROM trades WHERE product = $1 AND timestamp >= $2 ` +
|
||||||
|
`AND timestamp < $3;`
|
||||||
|
)
|
||||||
|
|
||||||
|
type TradeService struct {
|
||||||
|
client *Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TradeService) Trade(id int) (*tacitus.Trade, error) {
|
||||||
|
var tr tacitus.Trade
|
||||||
|
|
||||||
|
row := t.client.db.QueryRow(trade_find, id)
|
||||||
|
if err := row.Scan(&tr.Id, &tr.TradeId, &tr.Product, &tr.Price, &tr.Size, &tr.Buy,
|
||||||
|
&tr.Sell, &tr.Timestamp); err != nil {
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TradeService) TradeByTradeId(id int, prod string) (*tacitus.Trade, error) {
|
||||||
|
var tr tacitus.Trade
|
||||||
|
|
||||||
|
row := t.client.db.QueryRow(trade_find_trade_id, id, prod)
|
||||||
|
if err := row.Scan(&tr.Id, &tr.TradeId, &tr.Product, &tr.Price, &tr.Size, &tr.Buy,
|
||||||
|
&tr.Sell, &tr.Timestamp); err != nil {
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &tr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TradeService) CreateTrade(trade *tacitus.Trade) error {
|
||||||
|
var id int
|
||||||
|
res := t.client.db.QueryRow(trade_insert, trade.TradeId, trade.Product,
|
||||||
|
trade.Price, trade.Size, trade.Buy, trade.Sell, trade.Timestamp)
|
||||||
|
if err := res.Scan(&id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
trade.Id = id
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TradeService) DeleteTrade(id int) {
|
||||||
|
t.client.db.Exec(trade_delete, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TradeService) TradesInDateRange(product string, start,
|
||||||
|
end time.Time) ([]tacitus.Trade, error) {
|
||||||
|
|
||||||
|
trades := make([]tacitus.Trade, 0)
|
||||||
|
|
||||||
|
rows, err := t.client.db.Query(trades_in_date_range, product, start, end)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var trade tacitus.Trade
|
||||||
|
if err = rows.Scan(&trade.Id, &trade.TradeId, &trade.Product, &trade.Price,
|
||||||
|
&trade.Size, &trade.Buy, &trade.Sell, &trade.Timestamp); err != nil {
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
trades = append(trades, trade)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return nil, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
return trades, nil
|
||||||
|
}
|
26
trade.go
Normal file
26
trade.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package tacitus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/shopspring/decimal"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Trade struct {
|
||||||
|
Id int
|
||||||
|
TradeId int
|
||||||
|
Product string
|
||||||
|
Price decimal.Decimal
|
||||||
|
Size decimal.Decimal
|
||||||
|
Buy bool
|
||||||
|
Sell bool
|
||||||
|
Timestamp time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type TradeService interface {
|
||||||
|
Trade(id int) (*Trade, error)
|
||||||
|
TradeByTradeId(id int, prod string) (*Trade, error)
|
||||||
|
CreateTrade(t *Trade) error
|
||||||
|
DeleteTrade(id int)
|
||||||
|
TradesInDateRange(product string, start, end time.Time) ([]Trade, error)
|
||||||
|
}
|
Reference in New Issue
Block a user