From 67a1d7ee4beaab84087d56c9690582d52eb6e144 Mon Sep 17 00:00:00 2001 From: Frank Zhou <529808348@qq.com> Date: Sat, 29 Feb 2020 18:14:16 +0800 Subject: [PATCH] Update interfaces --- models/accountsummary.go => accountsummary.go | 2 +- backtest/backtest.go | 16 +++--- models/bar.go => bar.go | 2 +- broker.go | 4 -- brokers/bitmex-broker/broker.go | 2 +- brokers/bitmex-broker/broker_test.go | 4 +- brokers/bitmex-sim-broker/helper.go | 10 ++-- brokers/bitmex-sim-broker/simbroker.go | 35 +++++------- brokers/bybit-broker/broker.go | 2 +- brokers/deribit-broker/broker.go | 9 ++- brokers/deribit-broker/broker_test.go | 33 +++++++++-- brokers/deribit-broker/orderbook_local.go | 2 +- brokers/deribit-broker/orderbook_manager.go | 2 +- brokers/deribit-sim-broker/helper.go | 10 ++-- brokers/deribit-sim-broker/helper_test.go | 4 +- brokers/deribit-sim-broker/simbroker.go | 35 +++++------- models/consts.go => consts.go | 51 ++++++++++++++++- data/csv_dataloader.go | 52 ++++++++++++------ data/data.go | 8 ++- data/dataloader.go | 6 +- models/event.go => event.go | 2 +- models/orderbook.go | 33 ----------- models/tick.go | 23 -------- models/order.go => order.go | 2 +- orderbook.go | 55 +++++++++++++++++++ models/position.go => position.go | 2 +- 26 files changed, 242 insertions(+), 164 deletions(-) rename models/accountsummary.go => accountsummary.go (83%) rename models/bar.go => bar.go (86%) rename models/consts.go => consts.go (53%) rename models/event.go => event.go (96%) delete mode 100644 models/orderbook.go delete mode 100644 models/tick.go rename models/order.go => order.go (97%) create mode 100644 orderbook.go rename models/position.go => position.go (97%) diff --git a/models/accountsummary.go b/accountsummary.go similarity index 83% rename from models/accountsummary.go rename to accountsummary.go index 63693dc..b35e522 100644 --- a/models/accountsummary.go +++ b/accountsummary.go @@ -1,4 +1,4 @@ -package models +package gotrader type AccountSummary struct { Balance float64 diff --git a/backtest/backtest.go b/backtest/backtest.go index 45bd3c9..a7f735a 100644 --- a/backtest/backtest.go +++ b/backtest/backtest.go @@ -57,8 +57,8 @@ func (b *Backtest) runEventLoopOnce() { } func (b *Backtest) addItemStats() { - tick := b.data.GetTick() - tm := tick.Timestamp + ob := b.data.GetOrderBook() + tm := ob.Time update := false timestamp := time.Date(tm.Year(), tm.Month(), tm.Day(), tm.Hour(), tm.Minute()+1, 0, 0, time.UTC) var lastItem *LogItem @@ -73,17 +73,17 @@ func (b *Backtest) addItemStats() { var item *LogItem if update { item = lastItem - item.RawTime = tick.Timestamp - item.Ask = tick.Ask - item.Bid = tick.Bid + item.RawTime = ob.Time + item.Ask = ob.AskPrice() + item.Bid = ob.BidPrice() item.Stats = nil b.fetchItemStats(item) } else { item = &LogItem{ Time: timestamp, - RawTime: tick.Timestamp, - Ask: tick.Ask, - Bid: tick.Bid, + RawTime: ob.Time, + Ask: ob.AskPrice(), + Bid: ob.BidPrice(), Stats: nil, } b.fetchItemStats(item) diff --git a/models/bar.go b/bar.go similarity index 86% rename from models/bar.go rename to bar.go index 9d4bd43..4697595 100644 --- a/models/bar.go +++ b/bar.go @@ -1,4 +1,4 @@ -package models +package gotrader type Bar struct { Event diff --git a/broker.go b/broker.go index b23e25e..d221b80 100644 --- a/broker.go +++ b/broker.go @@ -1,9 +1,5 @@ package gotrader -import ( - . "github.com/coinrust/gotrader/models" -) - type Broker interface { // 订阅事件 Subscribe(event string, param string, listener interface{}) diff --git a/brokers/bitmex-broker/broker.go b/brokers/bitmex-broker/broker.go index 052b22a..fa903ca 100644 --- a/brokers/bitmex-broker/broker.go +++ b/brokers/bitmex-broker/broker.go @@ -1,7 +1,7 @@ package bitmex_broker import ( - . "github.com/coinrust/gotrader/models" + . "github.com/coinrust/gotrader" "github.com/frankrap/bitmex-api" "github.com/frankrap/bitmex-api/swagger" "strings" diff --git a/brokers/bitmex-broker/broker_test.go b/brokers/bitmex-broker/broker_test.go index 369f047..a667208 100644 --- a/brokers/bitmex-broker/broker_test.go +++ b/brokers/bitmex-broker/broker_test.go @@ -1,7 +1,7 @@ package bitmex_broker import ( - "github.com/coinrust/gotrader/models" + . "github.com/coinrust/gotrader" "github.com/frankrap/bitmex-api" "testing" ) @@ -26,7 +26,7 @@ func TestBitMEXBroker_GetOrderBook(t *testing.T) { func TestBitMEXBroker_PlaceOrder(t *testing.T) { b := newBrokerForTest() - order, err := b.PlaceOrder("XBTUSD", models.Buy, models.OrderTypeLimit, 8000, 10, true, false) + order, err := b.PlaceOrder("XBTUSD", Buy, OrderTypeLimit, 8000, 0, 10, true, false) if err != nil { t.Error(err) return diff --git a/brokers/bitmex-sim-broker/helper.go b/brokers/bitmex-sim-broker/helper.go index 4d55215..e4c0d29 100644 --- a/brokers/bitmex-sim-broker/helper.go +++ b/brokers/bitmex-sim-broker/helper.go @@ -1,11 +1,13 @@ package bitmex_sim_broker -import "github.com/coinrust/gotrader/models" +import ( + "github.com/coinrust/gotrader" +) // 计算收益 // pnl: 收益(BTC/ETH) // pnlUsd: 收益(USD) -func CalcPnl(side models.Direction, positionSize float64, entryPrice float64, exitPrice float64) (pnl float64, pnlUsd float64) { +func CalcPnl(side gotrader.Direction, positionSize float64, entryPrice float64, exitPrice float64) (pnl float64, pnlUsd float64) { //side := "Short" // "Short" //positionSize := 3850.0 //entryPrice := 3850.0 @@ -15,10 +17,10 @@ func CalcPnl(side models.Direction, positionSize float64, entryPrice float64, ex if positionSize == 0 { return } - if side == models.Buy { + if side == gotrader.Buy { pnl = (((entryPrice - exitPrice) / exitPrice) * (positionSize / entryPrice)) * -1 pnlUsd = ((entryPrice - exitPrice) * (positionSize / entryPrice)) * -1 - } else if side == models.Sell { + } else if side == gotrader.Sell { pnl = ((entryPrice - exitPrice) / exitPrice) * (positionSize / entryPrice) pnlUsd = (entryPrice - exitPrice) * (positionSize / entryPrice) } diff --git a/brokers/bitmex-sim-broker/simbroker.go b/brokers/bitmex-sim-broker/simbroker.go index cd3613d..6b802a1 100644 --- a/brokers/bitmex-sim-broker/simbroker.go +++ b/brokers/bitmex-sim-broker/simbroker.go @@ -3,8 +3,8 @@ package bitmex_sim_broker import ( "errors" "fmt" + . "github.com/coinrust/gotrader" "github.com/coinrust/gotrader/data" - . "github.com/coinrust/gotrader/models" "github.com/coinrust/gotrader/util2" "log" "math" @@ -46,12 +46,12 @@ func (b *BitMEXSimBroker) GetAccountSummary(currency string) (result AccountSumm } position := b.getPosition(symbol) var price float64 - tick := b.data.GetTick() + ob := b.data.GetOrderBook() side := position.Side() if side == Buy { - price = tick.Ask + price = ob.AskPrice() } else if side == Sell { - price = tick.Bid + price = ob.BidPrice() } pnl, _ := CalcPnl(side, math.Abs(position.Size), position.AvgPrice, price) result.Pnl = pnl @@ -60,16 +60,7 @@ func (b *BitMEXSimBroker) GetAccountSummary(currency string) (result AccountSumm } func (b *BitMEXSimBroker) GetOrderBook(symbol string, depth int) (result OrderBook, err error) { - tick := b.data.GetTick() - result.Time = tick.Timestamp - result.Asks = []Item{{ - Price: tick.Ask, - Amount: float64(tick.AskVolume), - }} - result.Bids = []Item{{ - Price: tick.Bid, - Amount: float64(tick.BidVolume), - }} + result = *b.data.GetOrderBook() return } @@ -135,7 +126,7 @@ func (b *BitMEXSimBroker) matchMarketOrder(order *Order) (err error) { return } - tick := b.data.GetTick() + ob := b.data.GetOrderBook() // 判断开仓数量 margin := b.balance @@ -147,13 +138,13 @@ func (b *BitMEXSimBroker) matchMarketOrder(order *Order) (err error) { // 市价成交 if order.Direction == Buy { - maxSize = margin * 100 * tick.Ask + maxSize = margin * 100 * ob.AskPrice() if order.Size > maxSize { err = errors.New(fmt.Sprintf("Rejected, maximum size of future position is %v", maxSize)) return } - price := tick.Ask + price := ob.AskPrice() size := order.Size // trade fee @@ -165,13 +156,13 @@ func (b *BitMEXSimBroker) matchMarketOrder(order *Order) (err error) { // Update position b.updatePosition(order.Symbol, size, price) } else if order.Direction == Sell { - maxSize = margin * 100 * tick.Bid + maxSize = margin * 100 * ob.BidPrice() if order.Size > maxSize { err = errors.New(fmt.Sprintf("Rejected, maximum size of future position is %v", maxSize)) return } - price := tick.Bid + price := ob.BidPrice() size := order.Size // trade fee @@ -191,9 +182,9 @@ func (b *BitMEXSimBroker) matchLimitOrder(order *Order, immediate bool) (err err return } - tick := b.data.GetTick() + ob := b.data.GetOrderBook() if order.Direction == Buy { // Bid order - if order.Price >= tick.Ask { + if order.Price >= ob.AskPrice() { if immediate && order.PostOnly { order.Status = OrderStatusRejected return @@ -217,7 +208,7 @@ func (b *BitMEXSimBroker) matchLimitOrder(order *Order, immediate bool) (err err b.updatePosition(order.Symbol, size, order.Price) } } else { // Ask order - if order.Price <= tick.Bid { + if order.Price <= ob.BidPrice() { if immediate && order.PostOnly { order.Status = OrderStatusRejected return diff --git a/brokers/bybit-broker/broker.go b/brokers/bybit-broker/broker.go index a6476fc..575868e 100644 --- a/brokers/bybit-broker/broker.go +++ b/brokers/bybit-broker/broker.go @@ -2,7 +2,7 @@ package bybit_broker import ( "errors" - . "github.com/coinrust/gotrader/models" + . "github.com/coinrust/gotrader" "github.com/frankrap/bybit-api/rest" "strings" ) diff --git a/brokers/deribit-broker/broker.go b/brokers/deribit-broker/broker.go index 699046e..4881f95 100644 --- a/brokers/deribit-broker/broker.go +++ b/brokers/deribit-broker/broker.go @@ -2,7 +2,7 @@ package deribit_broker import ( "github.com/chuckpreslar/emission" - . "github.com/coinrust/gotrader/models" + . "github.com/coinrust/gotrader" "github.com/frankrap/deribit-api" "github.com/frankrap/deribit-api/models" "time" @@ -96,6 +96,7 @@ func (b *DiribitBroker) GetOrderBook(symbol string, depth int) (result OrderBook func (b *DiribitBroker) PlaceOrder(symbol string, direction Direction, orderType OrderType, price float64, stopPx float64, size float64, postOnly bool, reduceOnly bool) (result Order, err error) { var _orderType string + var trigger string if orderType == OrderTypeLimit { _orderType = models.OrderTypeLimit stopPx = 0 @@ -104,8 +105,10 @@ func (b *DiribitBroker) PlaceOrder(symbol string, direction Direction, orderType stopPx = 0 } else if orderType == OrderTypeStopLimit { _orderType = models.OrderTypeStopLimit + trigger = models.TriggerTypeLastPrice } else if orderType == OrderTypeStopMarket { _orderType = models.OrderTypeStopMarket + trigger = models.TriggerTypeLastPrice } if direction == Buy { var ret models.BuyResponse @@ -120,7 +123,7 @@ func (b *DiribitBroker) PlaceOrder(symbol string, direction Direction, orderType PostOnly: postOnly, ReduceOnly: reduceOnly, StopPrice: stopPx, - //Trigger: "", + Trigger: trigger, //Advanced: "", }) if err != nil { @@ -140,7 +143,7 @@ func (b *DiribitBroker) PlaceOrder(symbol string, direction Direction, orderType PostOnly: postOnly, ReduceOnly: reduceOnly, StopPrice: stopPx, - //Trigger: "", + Trigger: trigger, //Advanced: "", }) if err != nil { diff --git a/brokers/deribit-broker/broker_test.go b/brokers/deribit-broker/broker_test.go index a29b6e0..cbc97ef 100644 --- a/brokers/deribit-broker/broker_test.go +++ b/brokers/deribit-broker/broker_test.go @@ -1,24 +1,27 @@ package deribit_broker import ( - . "github.com/coinrust/gotrader/models" + . "github.com/coinrust/gotrader" "github.com/frankrap/deribit-api" "log" "testing" "time" ) -func TestDiribitBroker_GetOrderBook(t *testing.T) { +func newBroker() Broker { apiKey := "AsJTU16U" secretKey := "mM5_K8LVxztN6TjjYpv_cJVGQBvk4jglrEpqkw1b87U" b := NewBroker(deribit.TestBaseURL, apiKey, secretKey) + return b +} + +func TestDiribitBroker_GetOrderBook(t *testing.T) { + b := newBroker() b.GetOrderBook("BTC-PERPETUAL", 10) } func TestDiribitBroker_Subscribe(t *testing.T) { - apiKey := "AsJTU16U" - secretKey := "mM5_K8LVxztN6TjjYpv_cJVGQBvk4jglrEpqkw1b87U" - b := NewBroker(deribit.TestBaseURL, apiKey, secretKey) + b := newBroker() //event := "book.ETH-PERPETUAL.100.1.100ms" param := "book.BTC-PERPETUAL.100ms" b.Subscribe("orderbook", param, func(e *OrderBook) { @@ -29,3 +32,23 @@ func TestDiribitBroker_Subscribe(t *testing.T) { time.Sleep(1 * time.Second) } } + +func TestDiribitBroker_PlaceStopOrder(t *testing.T) { + b := newBroker() + order, err := b.PlaceOrder( + "BTC-PERPETUAL", + Buy, + OrderTypeStopMarket, + 0, + 8900, + 10, + false, + false, + ) + if err != nil { + t.Error(err) + return + } + t.Logf("%#v", order) + t.Logf("Status: %v", order.Status.String()) +} diff --git a/brokers/deribit-broker/orderbook_local.go b/brokers/deribit-broker/orderbook_local.go index f7dbb0f..3a84756 100644 --- a/brokers/deribit-broker/orderbook_local.go +++ b/brokers/deribit-broker/orderbook_local.go @@ -1,7 +1,7 @@ package deribit_broker import ( - . "github.com/coinrust/gotrader/models" + . "github.com/coinrust/gotrader" "github.com/frankrap/deribit-api/models" "sort" "strconv" diff --git a/brokers/deribit-broker/orderbook_manager.go b/brokers/deribit-broker/orderbook_manager.go index 5e7e27c..d8c76b1 100644 --- a/brokers/deribit-broker/orderbook_manager.go +++ b/brokers/deribit-broker/orderbook_manager.go @@ -1,7 +1,7 @@ package deribit_broker import ( - . "github.com/coinrust/gotrader/models" + . "github.com/coinrust/gotrader" "github.com/frankrap/deribit-api/models" "sync" ) diff --git a/brokers/deribit-sim-broker/helper.go b/brokers/deribit-sim-broker/helper.go index ab44af1..9ee25b6 100644 --- a/brokers/deribit-sim-broker/helper.go +++ b/brokers/deribit-sim-broker/helper.go @@ -1,6 +1,8 @@ package deribit_sim_broker -import "github.com/coinrust/gotrader/models" +import ( + . "github.com/coinrust/gotrader" +) //im := (0.01 + sizeCurrency*0.00005) * sizeCurrency //t.Logf("IM: %v/%v", 0, im) // 保留9位小数,四舍五入 @@ -12,7 +14,7 @@ import "github.com/coinrust/gotrader/models" // 计算收益 // pnl: 收益(BTC/ETH) // pnlUsd: 收益(USD) -func CalcPnl(side models.Direction, positionSize float64, entryPrice float64, exitPrice float64) (pnl float64, pnlUsd float64) { +func CalcPnl(side Direction, positionSize float64, entryPrice float64, exitPrice float64) (pnl float64, pnlUsd float64) { //side := "Short" // "Short" //positionSize := 3850.0 //entryPrice := 3850.0 @@ -22,10 +24,10 @@ func CalcPnl(side models.Direction, positionSize float64, entryPrice float64, ex if positionSize == 0 { return } - if side == models.Buy { + if side == Buy { pnl = (((entryPrice - exitPrice) / exitPrice) * (positionSize / entryPrice)) * -1 pnlUsd = ((entryPrice - exitPrice) * (positionSize / entryPrice)) * -1 - } else if side == models.Sell { + } else if side == Sell { pnl = ((entryPrice - exitPrice) / exitPrice) * (positionSize / entryPrice) pnlUsd = (entryPrice - exitPrice) * (positionSize / entryPrice) } diff --git a/brokers/deribit-sim-broker/helper_test.go b/brokers/deribit-sim-broker/helper_test.go index 36c1bec..33d91a3 100644 --- a/brokers/deribit-sim-broker/helper_test.go +++ b/brokers/deribit-sim-broker/helper_test.go @@ -1,7 +1,7 @@ package deribit_sim_broker import ( - "github.com/coinrust/gotrader/models" + . "github.com/coinrust/gotrader" "testing" ) @@ -9,7 +9,7 @@ func TestCalcPnl(t *testing.T) { size := 50.0 entryPrice := 10351.5 exitPrice := 10348.5 - pnl, pnlUsd := CalcPnl(models.Buy, size, entryPrice, exitPrice) + pnl, pnlUsd := CalcPnl(Buy, size, entryPrice, exitPrice) t.Logf("pnl: %.8f", pnl) t.Logf("pnlUsd: %.8f", pnlUsd) } diff --git a/brokers/deribit-sim-broker/simbroker.go b/brokers/deribit-sim-broker/simbroker.go index 96d509b..c0fd621 100644 --- a/brokers/deribit-sim-broker/simbroker.go +++ b/brokers/deribit-sim-broker/simbroker.go @@ -3,8 +3,8 @@ package deribit_sim_broker import ( "errors" "fmt" + . "github.com/coinrust/gotrader" "github.com/coinrust/gotrader/data" - . "github.com/coinrust/gotrader/models" "github.com/coinrust/gotrader/util2" "log" "math" @@ -48,12 +48,12 @@ func (b *DiribitSimBroker) GetAccountSummary(currency string) (result AccountSum } position := b.getPosition(symbol) var price float64 - tick := b.data.GetTick() + ob := b.data.GetOrderBook() side := position.Side() if side == Buy { - price = tick.Ask + price = ob.AskPrice() } else if side == Sell { - price = tick.Bid + price = ob.BidPrice() } pnl, _ := CalcPnl(side, math.Abs(position.Size), position.AvgPrice, price) result.Pnl = pnl @@ -62,16 +62,7 @@ func (b *DiribitSimBroker) GetAccountSummary(currency string) (result AccountSum } func (b *DiribitSimBroker) GetOrderBook(symbol string, depth int) (result OrderBook, err error) { - tick := b.data.GetTick() - result.Time = tick.Timestamp - result.Asks = []Item{{ - Price: tick.Ask, - Amount: float64(tick.AskVolume), - }} - result.Bids = []Item{{ - Price: tick.Bid, - Amount: float64(tick.BidVolume), - }} + result = *b.data.GetOrderBook() return } @@ -143,7 +134,7 @@ func (b *DiribitSimBroker) matchMarketOrder(order *Order) (err error) { return } - tick := b.data.GetTick() + ob := b.data.GetOrderBook() // 判断开仓数量 margin := b.balance @@ -155,13 +146,13 @@ func (b *DiribitSimBroker) matchMarketOrder(order *Order) (err error) { // 市价成交 if order.Direction == Buy { - maxSize = margin * 100 * tick.Ask + maxSize = margin * 100 * ob.AskPrice() if order.Size > maxSize { err = errors.New(fmt.Sprintf("Rejected, maximum size of future position is %v", maxSize)) return } - price := tick.Ask + price := ob.AskPrice() size := order.Size // trade fee @@ -173,13 +164,13 @@ func (b *DiribitSimBroker) matchMarketOrder(order *Order) (err error) { // Update position b.updatePosition(order.Symbol, size, price) } else if order.Direction == Sell { - maxSize = margin * 100 * tick.Bid + maxSize = margin * 100 * ob.BidPrice() if order.Size > maxSize { err = errors.New(fmt.Sprintf("Rejected, maximum size of future position is %v", maxSize)) return } - price := tick.Bid + price := ob.BidPrice() size := order.Size // trade fee @@ -199,9 +190,9 @@ func (b *DiribitSimBroker) matchLimitOrder(order *Order, immediate bool) (err er return } - tick := b.data.GetTick() + ob := b.data.GetOrderBook() if order.Direction == Buy { // Bid order - if order.Price >= tick.Ask { + if order.Price >= ob.AskPrice() { if immediate && order.PostOnly { order.Status = OrderStatusRejected return @@ -225,7 +216,7 @@ func (b *DiribitSimBroker) matchLimitOrder(order *Order, immediate bool) (err er b.updatePosition(order.Symbol, size, order.Price) } } else { // Ask order - if order.Price <= tick.Bid { + if order.Price <= ob.BidPrice() { if immediate && order.PostOnly { order.Status = OrderStatusRejected return diff --git a/models/consts.go b/consts.go similarity index 53% rename from models/consts.go rename to consts.go index ee6f717..5189b27 100644 --- a/models/consts.go +++ b/consts.go @@ -1,4 +1,4 @@ -package models +package gotrader // Direction 委托/持仓方向 type Direction int @@ -8,6 +8,17 @@ const ( Sell // 做空 ) +func (d Direction) String() string { + switch d { + case Buy: + return "Buy" + case Sell: + return "Sell" + default: + return "None" + } +} + // OrderType 委托类型 type OrderType int @@ -18,6 +29,21 @@ const ( OrderTypeStopLimit // 限价止损单 ) +func (t OrderType) String() string { + switch t { + case OrderTypeMarket: + return "Market" + case OrderTypeLimit: + return "Limit" + case OrderTypeStopMarket: + return "StopMarket" + case OrderTypeStopLimit: + return "StopLimit" + default: + return "None" + } +} + // OrderStatus 委托状态 type OrderStatus int @@ -31,3 +57,26 @@ const ( OrderStatusUntriggered // 等待触发条件委托单 OrderStatusTriggered // 已触发条件单 ) + +func (s OrderStatus) String() string { + switch s { + case OrderStatusCreated: + return "Created" + case OrderStatusRejected: + return "Rejected" + case OrderStatusNew: + return "New" + case OrderStatusPartiallyFilled: + return "PartiallyFilled" + case OrderStatusFilled: + return "Filled" + case OrderStatusCancelled: + return "Cancelled" + case OrderStatusUntriggered: + return "Untriggered" + case OrderStatusTriggered: + return "Triggered" + default: + return "None" + } +} diff --git a/data/csv_dataloader.go b/data/csv_dataloader.go index 2bf994d..708a2f9 100644 --- a/data/csv_dataloader.go +++ b/data/csv_dataloader.go @@ -2,7 +2,7 @@ package data import ( "bufio" - "github.com/coinrust/gotrader/models" + . "github.com/coinrust/gotrader" "io" "log" "os" @@ -18,7 +18,7 @@ type CsvDataLoader struct { hasMoreData bool } -func (l *CsvDataLoader) ReadData() (result []*models.Tick) { +func (l *CsvDataLoader) ReadData() (result []*OrderBook) { if !l.hasMoreData { return nil } @@ -72,14 +72,18 @@ func (l *CsvDataLoader) close() { l.hasMoreData = false } -func (l *CsvDataLoader) readLine(line string) (result *models.Tick, ok bool) { +func (l *CsvDataLoader) readLine(line string) (result *OrderBook, ok bool) { ss := strings.Split(line, ",") - if len(ss) != 41 { + n := len(ss) + if n < 5 { //log.Printf("End [line: %v]", line) return } + if (n-1)%4 != 0 { + return + } - // 或略标题行 + // 忽略标题行 if ss[0] == "t" { ok = true return @@ -91,19 +95,33 @@ func (l *CsvDataLoader) readLine(line string) (result *models.Tick, ok bool) { } timestamp := time.Unix(0, t*1e6) - ask, _ := strconv.ParseFloat(ss[1], 64) - askAmount, _ := strconv.ParseFloat(ss[2], 64) - bid, _ := strconv.ParseFloat(ss[1+20], 64) - bidAmount, _ := strconv.ParseFloat(ss[2+20], 64) - - // log.Printf("Ask,AskAmount,Bid,BidAmount=%v/%v/%v/%v", ask, askAmount, bid, bidAmount) + nDepth := (n - 1) / 4 + + var asks []Item + var bids []Item + + bidOffset := nDepth * 2 + + for i := 0; i < nDepth; i++ { + ask, _ := strconv.ParseFloat(ss[1+i], 64) + askAmount, _ := strconv.ParseFloat(ss[2+i], 64) + bid, _ := strconv.ParseFloat(ss[1+i+bidOffset], 64) + bidAmount, _ := strconv.ParseFloat(ss[2+i+bidOffset], 64) + asks = append(asks, Item{ + Price: ask, + Amount: askAmount, + }) + bids = append(bids, Item{ + Price: bid, + Amount: bidAmount, + }) + } - result = &models.Tick{ - Timestamp: timestamp, - Bid: bid, - Ask: ask, - BidVolume: int64(bidAmount), - AskVolume: int64(askAmount), + result = &OrderBook{ + Time: timestamp, + Symbol: "", + Asks: asks, + Bids: bids, } ok = true return diff --git a/data/data.go b/data/data.go index 1e7112e..9750ed9 100644 --- a/data/data.go +++ b/data/data.go @@ -1,11 +1,13 @@ package data -import "github.com/coinrust/gotrader/models" +import ( + . "github.com/coinrust/gotrader" +) type Data struct { index int maxIndex int - data []*models.Tick + data []*OrderBook dataLoader DataLoader } @@ -27,7 +29,7 @@ func (d *Data) Reset() { d.maxIndex = len(d.data) - 1 } -func (d *Data) GetTick() *models.Tick { +func (d *Data) GetOrderBook() *OrderBook { return d.data[d.index] } diff --git a/data/dataloader.go b/data/dataloader.go index 659eeb0..8bc7bef 100644 --- a/data/dataloader.go +++ b/data/dataloader.go @@ -1,8 +1,10 @@ package data -import "github.com/coinrust/gotrader/models" +import ( + . "github.com/coinrust/gotrader" +) type DataLoader interface { - ReadData() []*models.Tick + ReadData() []*OrderBook HasMoreData() bool } diff --git a/models/event.go b/event.go similarity index 96% rename from models/event.go rename to event.go index 33d8974..6d8598b 100644 --- a/models/event.go +++ b/event.go @@ -1,4 +1,4 @@ -package models +package gotrader import "time" diff --git a/models/orderbook.go b/models/orderbook.go deleted file mode 100644 index 08d7378..0000000 --- a/models/orderbook.go +++ /dev/null @@ -1,33 +0,0 @@ -package models - -import ( - "time" -) - -type Item struct { - Price float64 - Amount float64 -} - -type OrderBook struct { - Symbol string - Time time.Time - Asks []Item - Bids []Item -} - -// Ask 卖一 -func (o *OrderBook) Ask() (result Item) { - if len(o.Asks) > 0 { - result = o.Asks[0] - } - return -} - -// Bid 买一 -func (o *OrderBook) Bid() (result Item) { - if len(o.Bids) > 0 { - result = o.Bids[0] - } - return -} diff --git a/models/tick.go b/models/tick.go deleted file mode 100644 index d5e7712..0000000 --- a/models/tick.go +++ /dev/null @@ -1,23 +0,0 @@ -package models - -import "time" - -type Tick struct { - // Event - Timestamp time.Time - Bid float64 - Ask float64 - BidVolume int64 - AskVolume int64 -} - -// Price returns the middle of Bid and Ask. -func (t Tick) Price() float64 { - latest := (t.Bid + t.Ask) / float64(2) - return latest -} - -// Spread returns the difference or spread of Bid and Ask. -func (t Tick) Spread() float64 { - return t.Bid - t.Ask -} diff --git a/models/order.go b/order.go similarity index 97% rename from models/order.go rename to order.go index 29f13a8..0966657 100644 --- a/models/order.go +++ b/order.go @@ -1,4 +1,4 @@ -package models +package gotrader // Order 委托 type Order struct { diff --git a/orderbook.go b/orderbook.go new file mode 100644 index 0000000..e37083b --- /dev/null +++ b/orderbook.go @@ -0,0 +1,55 @@ +package gotrader + +import ( + "time" +) + +type Item struct { + Price float64 + Amount float64 +} + +type OrderBook struct { + Symbol string + Time time.Time + Asks []Item + Bids []Item +} + +// Ask 卖一 +func (o *OrderBook) Ask() (result Item) { + if len(o.Asks) > 0 { + result = o.Asks[0] + } + return +} + +// Bid 买一 +func (o *OrderBook) Bid() (result Item) { + if len(o.Bids) > 0 { + result = o.Bids[0] + } + return +} + +// AskPrice 卖一价 +func (o *OrderBook) AskPrice() (result float64) { + if len(o.Asks) > 0 { + result = o.Asks[0].Price + } + return +} + +// BidPrice 买一价 +func (o *OrderBook) BidPrice() (result float64) { + if len(o.Bids) > 0 { + result = o.Bids[0].Price + } + return +} + +// Price returns the middle of Bid and Ask. +func (o *OrderBook) Price() float64 { + latest := (o.Bid().Price + o.Ask().Price) / float64(2) + return latest +} diff --git a/models/position.go b/position.go similarity index 97% rename from models/position.go rename to position.go index a30b02b..987bf34 100644 --- a/models/position.go +++ b/position.go @@ -1,4 +1,4 @@ -package models +package gotrader import "time"