mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-25 16:25:16 +00:00
commit
57f0888c3c
|
@ -22,4 +22,3 @@ func SaveConfigFile(filename string, v interface{}) error {
|
|||
|
||||
return ioutil.WriteFile(filename, out, 0644)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
type MovingAverageIndicator struct {
|
||||
store *MarketDataStore
|
||||
store *MarketDataStore
|
||||
Period int
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ func NewMovingAverageIndicator(period int) *MovingAverageIndicator {
|
|||
}
|
||||
|
||||
func (i *MovingAverageIndicator) handleUpdate(kline types.KLine) {
|
||||
klines, ok := i.store.KLineWindows[ Interval(kline.Interval) ]
|
||||
klines, ok := i.store.KLineWindows[Interval(kline.Interval)]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
@ -33,13 +33,13 @@ func (i *MovingAverageIndicator) handleUpdate(kline types.KLine) {
|
|||
|
||||
type IndicatorValue struct {
|
||||
Value float64
|
||||
Time time.Time
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
func calculateMovingAverage(klines types.KLineWindow, period int) (values []IndicatorValue) {
|
||||
for idx := range klines[period:] {
|
||||
offset := idx + period
|
||||
sum := klines[offset - period:offset].ReduceClose()
|
||||
sum := klines[offset-period : offset].ReduceClose()
|
||||
values = append(values, IndicatorValue{
|
||||
Time: klines[offset].GetEndTime(),
|
||||
Value: math.Round(sum / float64(period)),
|
||||
|
@ -48,16 +48,9 @@ func calculateMovingAverage(klines types.KLineWindow, period int) (values []Indi
|
|||
return values
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
func (i *MovingAverageIndicator) SubscribeStore(store *MarketDataStore) {
|
||||
i.store = store
|
||||
|
||||
// register kline update callback
|
||||
store.OnUpdate(i.handleUpdate)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package bbgo
|
||||
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
|
@ -9,9 +8,7 @@ import (
|
|||
|
||||
func TestCalculateMovingAverage(t *testing.T) {
|
||||
klines := types.KLineWindow{
|
||||
{
|
||||
|
||||
},
|
||||
{},
|
||||
}
|
||||
_ = klines
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ type BackTestStream struct {
|
|||
types.StandardStream
|
||||
}
|
||||
|
||||
|
||||
func (s *BackTestStream) Connect(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
package bbgo
|
||||
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
package bbgo
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package bbgo
|
||||
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
@ -9,5 +8,4 @@ func TestOrderProcessor(t *testing.T) {
|
|||
processor := &OrderProcessor{}
|
||||
_ = processor
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
package bbgo
|
||||
|
||||
|
|
|
@ -15,4 +15,3 @@ type NullNotifier struct{}
|
|||
|
||||
func (n *NullNotifier) Notify(format string, args ...interface{}) {
|
||||
}
|
||||
|
||||
|
|
|
@ -61,23 +61,23 @@ type StockManager struct {
|
|||
}
|
||||
|
||||
type Distribution struct {
|
||||
PriceLevels []string `json:"priceLevels"`
|
||||
TotalQuantity float64 `json:"totalQuantity"`
|
||||
Quantities map[string]float64 `json:"quantities"`
|
||||
Stocks map[string]StockSlice `json:"stocks"`
|
||||
PriceLevels []string `json:"priceLevels"`
|
||||
TotalQuantity float64 `json:"totalQuantity"`
|
||||
Quantities map[string]float64 `json:"quantities"`
|
||||
Stocks map[string]StockSlice `json:"stocks"`
|
||||
}
|
||||
|
||||
func (m *StockManager) Distribution(level int) *Distribution {
|
||||
var d = Distribution{
|
||||
Quantities: map[string]float64{},
|
||||
Stocks: map[string]StockSlice{},
|
||||
Stocks: map[string]StockSlice{},
|
||||
}
|
||||
|
||||
for _, stock := range m.Stocks {
|
||||
n := math.Ceil(math.Log10(stock.Price))
|
||||
digits := int(n - math.Max(float64(level), 1.0))
|
||||
div := math.Pow10(digits)
|
||||
priceLevel := math.Floor(stock.Price / div) * div
|
||||
priceLevel := math.Floor(stock.Price/div) * div
|
||||
key := strconv.FormatFloat(priceLevel, 'f', 2, 64)
|
||||
|
||||
d.TotalQuantity += stock.Quantity
|
||||
|
@ -98,7 +98,6 @@ func (m *StockManager) Distribution(level int) *Distribution {
|
|||
|
||||
sort.Float64s(priceLevels)
|
||||
|
||||
|
||||
return &d
|
||||
}
|
||||
|
||||
|
|
|
@ -52,16 +52,16 @@ func TestStockManager(t *testing.T) {
|
|||
assert.Len(t, stockManager.Stocks, 2)
|
||||
assert.Equal(t, StockSlice{
|
||||
{
|
||||
Symbol: "BTCUSDT",
|
||||
Price: 9100.0,
|
||||
Symbol: "BTCUSDT",
|
||||
Price: 9100.0,
|
||||
Quantity: 0.05,
|
||||
IsBuyer: true,
|
||||
IsBuyer: true,
|
||||
},
|
||||
{
|
||||
Symbol: "BTCUSDT",
|
||||
Price: 9100.0,
|
||||
Symbol: "BTCUSDT",
|
||||
Price: 9100.0,
|
||||
Quantity: 0.04,
|
||||
IsBuyer: true,
|
||||
IsBuyer: true,
|
||||
},
|
||||
}, stockManager.Stocks)
|
||||
assert.Len(t, stockManager.PendingSells, 0)
|
||||
|
@ -121,10 +121,10 @@ func TestStockManager(t *testing.T) {
|
|||
assert.Len(t, stockManager.Stocks, 1)
|
||||
assert.Equal(t, StockSlice{
|
||||
{
|
||||
Symbol: "BTCUSDT",
|
||||
Price: 9100.0,
|
||||
Symbol: "BTCUSDT",
|
||||
Price: 9100.0,
|
||||
Quantity: 0.02,
|
||||
IsBuyer: true,
|
||||
IsBuyer: true,
|
||||
},
|
||||
}, stockManager.Stocks)
|
||||
assert.Len(t, stockManager.PendingSells, 0)
|
||||
|
@ -146,10 +146,10 @@ func TestStockManager(t *testing.T) {
|
|||
assert.Len(t, stockManager.Stocks, 1)
|
||||
assert.Equal(t, StockSlice{
|
||||
{
|
||||
Symbol: "BTCUSDT",
|
||||
Price: 9100.0,
|
||||
Symbol: "BTCUSDT",
|
||||
Price: 9100.0,
|
||||
Quantity: 0.03,
|
||||
IsBuyer: true,
|
||||
IsBuyer: true,
|
||||
},
|
||||
}, stockManager.Stocks)
|
||||
assert.Len(t, stockManager.PendingSells, 0)
|
||||
|
@ -172,10 +172,10 @@ func TestStockManager(t *testing.T) {
|
|||
assert.Len(t, stockManager.PendingSells, 1)
|
||||
assert.Equal(t, StockSlice{
|
||||
{
|
||||
Symbol: "BTCUSDT",
|
||||
Price: 9200.0,
|
||||
Symbol: "BTCUSDT",
|
||||
Price: 9200.0,
|
||||
Quantity: 0.05,
|
||||
IsBuyer: false,
|
||||
IsBuyer: false,
|
||||
},
|
||||
}, stockManager.PendingSells)
|
||||
})
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
package cmd
|
||||
|
||||
|
|
|
@ -249,15 +249,15 @@ func ParseEvent(message string) (interface{}, error) {
|
|||
|
||||
type DepthEntry struct {
|
||||
PriceLevel string
|
||||
Quantity string
|
||||
Quantity string
|
||||
}
|
||||
|
||||
type DepthEvent struct {
|
||||
EventBase
|
||||
|
||||
Symbol string `json:"s"`
|
||||
FirstUpdateID int64 `json:"U"`
|
||||
FinalUpdateID int64 `json:"u"`
|
||||
Symbol string `json:"s"`
|
||||
FirstUpdateID int64 `json:"U"`
|
||||
FinalUpdateID int64 `json:"u"`
|
||||
|
||||
Bids []DepthEntry
|
||||
Asks []DepthEntry
|
||||
|
@ -319,7 +319,7 @@ func parseDepthEntry(val *fastjson.Value) (*DepthEntry, error) {
|
|||
|
||||
return &DepthEntry{
|
||||
PriceLevel: string(arr[0].GetStringBytes()),
|
||||
Quantity: string(arr[1].GetStringBytes()),
|
||||
Quantity: string(arr[1].GetStringBytes()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -330,7 +330,7 @@ func parseDepthEvent(val *fastjson.Value) (*DepthEvent, error) {
|
|||
Event: string(val.GetStringBytes("e")),
|
||||
Time: val.GetInt64("E"),
|
||||
},
|
||||
Symbol: string(val.GetStringBytes("s")),
|
||||
Symbol: string(val.GetStringBytes("s")),
|
||||
FirstUpdateID: val.GetInt64("U"),
|
||||
FinalUpdateID: val.GetInt64("u"),
|
||||
}
|
||||
|
|
|
@ -16,17 +16,15 @@ const (
|
|||
Closed
|
||||
)
|
||||
|
||||
|
||||
type OrderState string
|
||||
|
||||
const (
|
||||
OrderStateDone = OrderState("done")
|
||||
OrderStateCancel = OrderState("cancel")
|
||||
OrderStateWait = OrderState("wait")
|
||||
OrderStateDone = OrderState("done")
|
||||
OrderStateCancel = OrderState("cancel")
|
||||
OrderStateWait = OrderState("wait")
|
||||
OrderStateConvert = OrderState("convert")
|
||||
)
|
||||
|
||||
|
||||
type OrderType string
|
||||
|
||||
// Order types that the API can return.
|
||||
|
|
|
@ -88,7 +88,7 @@ type RestClient struct {
|
|||
AccountService *AccountService
|
||||
PublicService *PublicService
|
||||
TradeService *TradeService
|
||||
OrderService *OrderService
|
||||
OrderService *OrderService
|
||||
// OrderBookService *OrderBookService
|
||||
// MaxTokenService *MaxTokenService
|
||||
// MaxKLineService *KLineService
|
||||
|
@ -212,7 +212,6 @@ func (c *RestClient) newAuthenticatedRequest(m string, refURL string, data inter
|
|||
return nil, errors.New("empty api secret")
|
||||
}
|
||||
|
||||
|
||||
req, err := c.newRequest(m, refURL, nil, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -7,16 +7,16 @@ import (
|
|||
)
|
||||
|
||||
type MarkerInfo struct {
|
||||
Fee string `json:"fee"`
|
||||
Fee string `json:"fee"`
|
||||
FeeCurrency string `json:"fee_currency"`
|
||||
OrderID int `json:"order_id"`
|
||||
OrderID int `json:"order_id"`
|
||||
}
|
||||
|
||||
type TradeInfo struct {
|
||||
// Maker tells you the maker trade side
|
||||
Maker string `json:"maker,omitempty"`
|
||||
Bid *MarkerInfo `json:"bid,omitempty"`
|
||||
Ask *MarkerInfo `json:"ask,omitempty"`
|
||||
Maker string `json:"maker,omitempty"`
|
||||
Bid *MarkerInfo `json:"bid,omitempty"`
|
||||
Ask *MarkerInfo `json:"ask,omitempty"`
|
||||
}
|
||||
|
||||
// Trade represents one returned trade on the max platform.
|
||||
|
|
|
@ -180,8 +180,8 @@ func (m *BalanceMessage) Balance() (*types.Balance, error) {
|
|||
}
|
||||
|
||||
return &types.Balance{
|
||||
Currency: m.Currency,
|
||||
Locked: locked,
|
||||
Currency: m.Currency,
|
||||
Locked: locked,
|
||||
Available: available,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -54,13 +54,13 @@ type WebSocketService struct {
|
|||
errorEventCallbacks []func(e ErrorEvent)
|
||||
subscriptionEventCallbacks []func(e SubscriptionEvent)
|
||||
|
||||
tradeUpdateEventCallbacks []func(e TradeUpdateEvent)
|
||||
tradeSnapshotEventCallbacks []func(e TradeSnapshotEvent)
|
||||
orderUpdateEventCallbacks []func(e OrderUpdateEvent)
|
||||
orderSnapshotEventCallbacks []func(e OrderSnapshotEvent)
|
||||
tradeUpdateEventCallbacks []func(e TradeUpdateEvent)
|
||||
tradeSnapshotEventCallbacks []func(e TradeSnapshotEvent)
|
||||
orderUpdateEventCallbacks []func(e OrderUpdateEvent)
|
||||
orderSnapshotEventCallbacks []func(e OrderSnapshotEvent)
|
||||
|
||||
accountSnapshotEventCallbacks []func(e AccountSnapshotEvent)
|
||||
accountUpdateEventCallbacks []func(e AccountUpdateEvent)
|
||||
accountUpdateEventCallbacks []func(e AccountUpdateEvent)
|
||||
}
|
||||
|
||||
func NewWebSocketService(wsURL string, key, secret string) *WebSocketService {
|
||||
|
|
|
@ -71,7 +71,6 @@ func NewStream(key, secret string) *Stream {
|
|||
stream.EmitBalanceUpdate(snapshot)
|
||||
})
|
||||
|
||||
|
||||
return stream
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
type TradeSync struct {
|
||||
Service *TradeService
|
||||
Service *TradeService
|
||||
}
|
||||
|
||||
func (s *TradeSync) Sync(ctx context.Context, exchange types.Exchange, symbol string, startTime time.Time) error {
|
||||
|
@ -83,7 +83,7 @@ func (s *TradeService) QueryLast(symbol string) (*types.Trade, error) {
|
|||
|
||||
func (s *TradeService) QueryForTradingFeeCurrency(symbol string, feeCurrency string) ([]types.Trade, error) {
|
||||
rows, err := s.DB.NamedQuery(`SELECT * FROM trades WHERE symbol = :symbol OR fee_currency = :fee_currency ORDER BY traded_at ASC`, map[string]interface{}{
|
||||
"symbol": symbol,
|
||||
"symbol": symbol,
|
||||
"fee_currency": feeCurrency,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -108,7 +108,7 @@ func (s *TradeService) Query(symbol string) ([]types.Trade, error) {
|
|||
return s.scanRows(rows)
|
||||
}
|
||||
|
||||
func (s *TradeService) scanRows(rows *sqlx.Rows) (trades []types.Trade, err error) {
|
||||
func (s *TradeService) scanRows(rows *sqlx.Rows) (trades []types.Trade, err error) {
|
||||
for rows.Next() {
|
||||
var trade types.Trade
|
||||
if err := rows.StructScan(&trade); err != nil {
|
||||
|
@ -121,8 +121,6 @@ func (s *TradeService) scanRows(rows *sqlx.Rows) (trades []types.Trade, err err
|
|||
return trades, rows.Err()
|
||||
}
|
||||
|
||||
|
||||
|
||||
func (s *TradeService) Insert(trade types.Trade) error {
|
||||
_, err := s.DB.NamedExec(`
|
||||
INSERT INTO trades (id, exchange, symbol, price, quantity, quote_quantity, side, is_buyer, is_maker, fee, fee_currency, traded_at)
|
||||
|
|
|
@ -26,7 +26,6 @@ func (c Chan) Drain(duration, deadline time.Duration) (cnt int) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func (c Chan) Emit() {
|
||||
select {
|
||||
case c <- struct{}{}:
|
||||
|
|
|
@ -17,7 +17,7 @@ type LogHook struct {
|
|||
func NewLogHook(token string, channel string) *LogHook {
|
||||
var client = slack.New(token)
|
||||
return &LogHook{
|
||||
Slack: client,
|
||||
Slack: client,
|
||||
ErrorChannel: channel,
|
||||
}
|
||||
}
|
||||
|
@ -56,8 +56,8 @@ func (t *LogHook) Fire(e *logrus.Entry) error {
|
|||
}
|
||||
|
||||
slackAttachments = append(slackAttachments, slack.Attachment{
|
||||
Color: color,
|
||||
Title: strings.ToUpper(e.Level.String()),
|
||||
Color: color,
|
||||
Title: strings.ToUpper(e.Level.String()),
|
||||
Fields: fields,
|
||||
})
|
||||
|
||||
|
|
|
@ -11,5 +11,3 @@ func TrendIcon(trend int) string {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package types
|
||||
|
||||
type Balance struct {
|
||||
Currency string `json:"currency"`
|
||||
Currency string `json:"currency"`
|
||||
Available float64 `json:"available"`
|
||||
Locked float64 `json:"locked"`
|
||||
Locked float64 `json:"locked"`
|
||||
}
|
||||
|
||||
type BalanceMap map[string]Balance
|
||||
|
@ -11,8 +11,8 @@ type BalanceMap map[string]Balance
|
|||
type Account struct {
|
||||
MakerCommission int64
|
||||
TakerCommission int64
|
||||
AccountType string
|
||||
Balances map[string]Balance
|
||||
AccountType string
|
||||
Balances map[string]Balance
|
||||
}
|
||||
|
||||
func (a *Account) UpdateBalance(b Balance) {
|
||||
|
|
|
@ -2,4 +2,3 @@ package types
|
|||
|
||||
const Green = "#228B22"
|
||||
const Red = "#800000"
|
||||
|
||||
|
|
|
@ -5,4 +5,3 @@ import "github.com/leekchan/accounting"
|
|||
var USD = accounting.Accounting{Symbol: "$ ", Precision: 2}
|
||||
var BTC = accounting.Accounting{Symbol: "BTC ", Precision: 2}
|
||||
var BNB = accounting.Accounting{Symbol: "BNB ", Precision: 4}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ import (
|
|||
|
||||
func TestKLineWindow_Tail(t *testing.T) {
|
||||
var win = KLineWindow{
|
||||
{ Open: 11600.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
{ Open: 11600.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
{Open: 11600.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
{Open: 11600.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
}
|
||||
|
||||
var win2 = win.Tail(1)
|
||||
|
@ -24,10 +24,10 @@ func TestKLineWindow_Tail(t *testing.T) {
|
|||
|
||||
func TestKLineWindow_Truncate(t *testing.T) {
|
||||
var win = KLineWindow{
|
||||
{ Open: 11600.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
{ Open: 11601.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
{ Open: 11602.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
{ Open: 11603.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
{Open: 11600.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
{Open: 11601.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
{Open: 11602.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
{Open: 11603.0, Close: 11600.0, High: 11600.0, Low: 11600.0},
|
||||
}
|
||||
|
||||
win.Truncate(5)
|
||||
|
@ -38,7 +38,6 @@ func TestKLineWindow_Truncate(t *testing.T) {
|
|||
assert.Len(t, win, 3)
|
||||
assert.Equal(t, 11603.0, win.Last().Open)
|
||||
|
||||
|
||||
win.Truncate(1)
|
||||
assert.Len(t, win, 1)
|
||||
assert.Equal(t, 11603.0, win.Last().Open)
|
||||
|
|
|
@ -238,5 +238,3 @@ func (sb *StreamOrderBook) BindStream(stream Stream) {
|
|||
sb.C.Emit()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ var BookChannel = Channel("book")
|
|||
|
||||
var KLineChannel = Channel("kline")
|
||||
|
||||
|
||||
//go:generate callbackgen -type StandardStream -interface
|
||||
type StandardStream struct {
|
||||
Subscriptions []Subscription
|
||||
|
|
|
@ -40,13 +40,13 @@ func (trade Trade) SlackAttachment() slack.Attachment {
|
|||
market, ok := FindMarket(trade.Symbol)
|
||||
if !ok {
|
||||
return slack.Attachment{
|
||||
Text: fmt.Sprintf("*%s* Trade %s", trade.Symbol, trade.Side),
|
||||
Text: fmt.Sprintf("*%s* Trade %s", trade.Symbol, trade.Side),
|
||||
Color: color,
|
||||
}
|
||||
}
|
||||
|
||||
return slack.Attachment{
|
||||
Text: fmt.Sprintf("*%s* Trade %s", trade.Symbol, trade.Side),
|
||||
Text: fmt.Sprintf("*%s* Trade %s", trade.Symbol, trade.Side),
|
||||
Color: color,
|
||||
// Pretext: "",
|
||||
// Text: "",
|
||||
|
|
|
@ -30,7 +30,6 @@ func ParseFloat(s string) (float64, error) {
|
|||
return strconv.ParseFloat(s, 64)
|
||||
}
|
||||
|
||||
|
||||
func MustParseFloat(s string) float64 {
|
||||
if len(s) == 0 {
|
||||
return 0.0
|
||||
|
@ -52,4 +51,3 @@ func Zero(v float64) bool {
|
|||
func NotZero(v float64) bool {
|
||||
return math.Abs(v) > epsilon
|
||||
}
|
||||
|
||||
|
|
|
@ -20,4 +20,3 @@ func Render(tpl string, args interface{}) string {
|
|||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
|
|
|
@ -41,4 +41,3 @@ func (i *VolatileMemory) IsTextFresh(text string, ttl time.Duration) bool {
|
|||
i.textTimes[text] = now
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user