Browse Source

OrderIntent

Alexey Kim 2 days ago
parent
commit
27bb02f0c9
5 changed files with 229 additions and 4 deletions
  1. 2 2
      order.go
  2. 80 0
      order_intent.go
  3. 128 0
      order_intent_test.go
  4. 5 0
      util/etc.go
  5. 14 2
      util/order.go

+ 2 - 2
order.go

@@ -35,6 +35,6 @@ type Order interface {
 	GetPnL() float64
 	GetCurrency() string
 	GetStatus() string
-	
-	Intent() Side
+
+	GetIntent() OrderIntent
 }

+ 80 - 0
order_intent.go

@@ -0,0 +1,80 @@
+package sentio
+
+import (
+	"bytes"
+	"fmt"
+	"strconv"
+	"strings"
+	"unicode/utf8"
+)
+
+type OrderIntent int8
+
+const (
+	BuyToOpen OrderIntent = iota
+	BuyToClose
+	SellToOpen
+	SellToClose
+)
+
+var intents = map[OrderIntent]string{
+	BuyToOpen:   "buy_to_open",
+	BuyToClose:  "buy_to_close",
+	SellToOpen:  "sell_to_open",
+	SellToClose: "sell_to_close",
+}
+
+func (s OrderIntent) String() string {
+	if intent, ok := intents[s]; ok {
+		return intent
+	}
+
+	return "undefined"
+}
+
+func (s OrderIntent) MarshalJSON() ([]byte, error) {
+	intent := s.String()
+	return []byte(`"` + intent + `"`), nil
+}
+
+func (s *OrderIntent) UnmarshalJSON(data []byte) error {
+	var (
+		intent OrderIntent
+		runes  = bytes.Runes(data)
+		err    error
+	)
+
+	if l := utf8.RuneCount(data); runes != nil && runes[0] == 34 && runes[l-1] == 34 {
+		data = data[1 : l-1]
+		intent, err = ParseOrderIntent(string(data))
+	} else {
+		n, err := strconv.Atoi(string(data))
+		if err == nil {
+			intent = OrderIntent(n)
+		}
+	}
+
+	if err != nil {
+		return err
+	}
+
+	if _, ok := intents[intent]; !ok {
+		return fmt.Errorf("unknown `OrderIntent`: %s", data)
+	}
+
+	*s = intent
+	return nil
+}
+
+func ParseOrderIntent(s string) (OrderIntent, error) {
+	s = strings.TrimSpace(s)
+	s = strings.ToLower(s)
+
+	for intent, s2 := range intents {
+		if s == s2 {
+			return intent, nil
+		}
+	}
+
+	return -1, fmt.Errorf("`sentio.ParseOrderIntent`: undefined OrderIntent `%s`", s)
+}

+ 128 - 0
order_intent_test.go

@@ -0,0 +1,128 @@
+package sentio
+
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+)
+
+func TestOrderIntent_MarshalJSON(t *testing.T) {
+	type Obj struct {
+		Intent OrderIntent `json:"intent"`
+	}
+
+	tests := []struct {
+		obj  Obj
+		want []byte
+	}{
+		{
+			obj:  Obj{Intent: BuyToOpen},
+			want: []byte(`{"intent":"buy_to_open"}`),
+		},
+		{
+			obj:  Obj{Intent: BuyToClose},
+			want: []byte(`{"intent":"buy_to_close"}`),
+		},
+		{
+			obj:  Obj{Intent: SellToOpen},
+			want: []byte(`{"intent":"sell_to_open"}`),
+		},
+		{
+			obj:  Obj{Intent: SellToClose},
+			want: []byte(`{"intent":"sell_to_close"}`),
+		},
+		{
+			obj:  Obj{Intent: -1},
+			want: []byte(`{"intent":"undefined"}`),
+		},
+		{
+			obj:  Obj{Intent: 100},
+			want: []byte(`{"intent":"undefined"}`),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run("TestOrderIntent", func(t *testing.T) {
+			if got, _ := json.Marshal(tt.obj); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("MarshalJSON() got = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestOrderIntent_UnmarshalJSON(t *testing.T) {
+	type Obj struct {
+		Intent OrderIntent `json:"intent"`
+	}
+
+	tests := []struct {
+		obj     Obj
+		data    []byte
+		wantErr bool
+	}{
+		{
+			obj: Obj{
+				Intent: BuyToOpen,
+			},
+			data:    []byte(`{"intent":"buy_to_open"}`),
+			wantErr: false,
+		},
+		{
+			obj: Obj{
+				Intent: BuyToClose,
+			},
+			data:    []byte(`{"intent":"buy_to_close"}`),
+			wantErr: false,
+		},
+		{
+			obj: Obj{
+				Intent: SellToOpen,
+			},
+			data:    []byte(`{"intent":"sell_to_open"}`),
+			wantErr: false,
+		},
+		{
+			obj: Obj{
+				Intent: SellToClose,
+			},
+			data:    []byte(`{"intent":"sell_to_close"}`),
+			wantErr: false,
+		},
+		{
+			obj: Obj{
+				Intent: SellToClose,
+			},
+			data:    []byte(`{"intent":3}`),
+			wantErr: false,
+		},
+		{
+			obj: Obj{
+				Intent: BuyToOpen,
+			},
+			data:    []byte(`{"intent":19}`),
+			wantErr: true,
+		},
+		{
+			obj: Obj{
+				Intent: BuyToOpen,
+			},
+			data:    []byte(`{"intent":null}`),
+			wantErr: false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run("TestOrderIntent", func(t *testing.T) {
+			var v Obj
+
+			if err := json.Unmarshal(tt.data, &v); (err != nil) != tt.wantErr {
+				t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+
+			if !reflect.DeepEqual(v, tt.obj) {
+				t.Errorf("MarshalJSON() got = %v, want %v", v, tt.obj)
+			}
+		})
+	}
+}

+ 5 - 0
util/etc.go

@@ -0,0 +1,5 @@
+package util
+
+func ToPtr[T any](x T) *T {
+	return &x
+}

+ 14 - 2
util/order.go

@@ -2,14 +2,26 @@ package util
 
 import (
 	"errors"
+	"fmt"
 	"git.beejay.kim/Gshopper/sentio"
 	"math"
 )
 
 const TradingRange = .02
 
-func ToPtr[T any](x T) *T {
-	return &x
+func SideOf(intent sentio.OrderIntent) sentio.Side {
+	switch intent {
+	case sentio.BuyToOpen:
+		return sentio.LONG
+	case sentio.SellToClose:
+		return sentio.LONG
+	case sentio.BuyToClose:
+		return sentio.SHORT
+	case sentio.SellToOpen:
+		return sentio.SHORT
+	}
+
+	panic(fmt.Errorf("unexpected `OrderIntent`: %v", intent))
 }
 
 func NewOrder(