Ver Fonte

v2.1

- draft
Alexey Kim há 7 meses atrás
pai
commit
bb589ee922
12 ficheiros alterados com 352 adições e 207 exclusões
  1. 8 7
      Makefile
  2. 2 0
      go.mod
  3. 6 0
      go.sum
  4. 12 0
      number.go
  5. 19 4
      probability.go
  6. 223 0
      probability.pb.go
  7. 14 0
      proto/probability.proto
  8. 30 0
      proto/probability_pb2.py
  9. 2 2
      strategy.go
  10. 0 144
      strategy/alpaca/qqq15_nodelay/strategy.go
  11. 19 18
      strategy/alpaca/qqq200/strategy.go
  12. 17 32
      util/order.go

+ 8 - 7
Makefile

@@ -19,13 +19,7 @@ ifeq ($(DEBUG),1)
 endif
 
 .PHONY: all
-all: gen deps fmt test
-
-.PHONY: gen
-gen:
-	-@echo "-> $@"
-	-@echo "=== Generate strategies"
-	$(foreach file, $(filter-out $(wildcard ./strategy/*_test.go), $(wildcard ./strategy/*.go)), CGO_ENABLED=1 go build -ldflags=$(LDFLAGS) -buildmode=plugin -gcflags=$(GCFLAGS) -o $(file).so $(file);)
+all: deps fmt test
 
 .PHONY: deps
 deps:
@@ -42,3 +36,10 @@ test:
 	-@echo "-> $@"
 	CGO_ENABLED=1 go test $(shell go list ./...)
 	-go vet $(shell go list ./...)
+
+.PHONE: gen-proto
+gen-proto:
+	-@echo "-> $@"
+	@protoc -I=. --go_out=paths=source_relative:. --python_out=. proto/*.proto
+	@cp proto/*.pb.go ./
+	-@rm proto/*.pb.go

+ 2 - 0
go.mod

@@ -1,3 +1,5 @@
 module git.beejay.kim/Gshopper/sentio
 
 go 1.22.2
+
+require google.golang.org/protobuf v1.35.2

+ 6 - 0
go.sum

@@ -0,0 +1,6 @@
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
+google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=

+ 12 - 0
number.go

@@ -0,0 +1,12 @@
+package sentio
+
+import "math"
+
+func round(num float64) int {
+	return int(num + math.Copysign(0.5, num))
+}
+
+func ToFixed(num float64, precision int) float64 {
+	output := math.Pow(10, float64(precision))
+	return float64(round(num*output)) / output
+}

+ 19 - 4
probability.go

@@ -1,8 +1,23 @@
 package sentio
 
-import "time"
+import (
+	"cmp"
+	"slices"
+)
 
-type Probability struct {
-	TS    time.Time
-	Value float64
+func CompareProbabilityStamps(a, b *Probability_Stamp) int {
+	return cmp.Compare(a.Time.AsTime().Unix(), b.Time.AsTime().Unix())
+}
+
+func (x *Probability) First() (*Probability_Stamp, bool) {
+	return x.Next(0)
+}
+
+func (x *Probability) Next(n int) (*Probability_Stamp, bool) {
+	if x.Stamps == nil || len(x.Stamps) < n {
+		return nil, false
+	}
+
+	slices.SortFunc(x.Stamps, CompareProbabilityStamps)
+	return x.Stamps[n], true
 }

+ 223 - 0
probability.pb.go

@@ -0,0 +1,223 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.32.0
+// 	protoc        v4.25.1
+// source: proto/probability.proto
+
+package sentio
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Probability struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Stamps []*Probability_Stamp `protobuf:"bytes,10,rep,name=stamps,proto3" json:"stamps,omitempty"`
+}
+
+func (x *Probability) Reset() {
+	*x = Probability{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proto_probability_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Probability) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Probability) ProtoMessage() {}
+
+func (x *Probability) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_probability_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Probability.ProtoReflect.Descriptor instead.
+func (*Probability) Descriptor() ([]byte, []int) {
+	return file_proto_probability_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Probability) GetStamps() []*Probability_Stamp {
+	if x != nil {
+		return x.Stamps
+	}
+	return nil
+}
+
+type Probability_Stamp struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Time  *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=time,proto3" json:"time,omitempty"`
+	Value float32                `protobuf:"fixed32,2,opt,name=value,proto3" json:"value,omitempty"`
+}
+
+func (x *Probability_Stamp) Reset() {
+	*x = Probability_Stamp{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proto_probability_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Probability_Stamp) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Probability_Stamp) ProtoMessage() {}
+
+func (x *Probability_Stamp) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_probability_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Probability_Stamp.ProtoReflect.Descriptor instead.
+func (*Probability_Stamp) Descriptor() ([]byte, []int) {
+	return file_proto_probability_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *Probability_Stamp) GetTime() *timestamppb.Timestamp {
+	if x != nil {
+		return x.Time
+	}
+	return nil
+}
+
+func (x *Probability_Stamp) GetValue() float32 {
+	if x != nil {
+		return x.Value
+	}
+	return 0
+}
+
+var File_proto_probability_proto protoreflect.FileDescriptor
+
+var file_proto_probability_proto_rawDesc = []byte{
+	0x0a, 0x17, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c,
+	0x69, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x69,
+	0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x22, 0x8f, 0x01, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69,
+	0x74, 0x79, 0x12, 0x31, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x0a, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x19, 0x2e, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x2e, 0x50, 0x72, 0x6f, 0x62,
+	0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x06, 0x73,
+	0x74, 0x61, 0x6d, 0x70, 0x73, 0x1a, 0x4d, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2e,
+	0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
+	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
+	0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x14,
+	0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x73, 0x65, 0x6e, 0x74, 0x69, 0x6f,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_proto_probability_proto_rawDescOnce sync.Once
+	file_proto_probability_proto_rawDescData = file_proto_probability_proto_rawDesc
+)
+
+func file_proto_probability_proto_rawDescGZIP() []byte {
+	file_proto_probability_proto_rawDescOnce.Do(func() {
+		file_proto_probability_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_probability_proto_rawDescData)
+	})
+	return file_proto_probability_proto_rawDescData
+}
+
+var file_proto_probability_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_proto_probability_proto_goTypes = []interface{}{
+	(*Probability)(nil),           // 0: sentio.Probability
+	(*Probability_Stamp)(nil),     // 1: sentio.Probability.Stamp
+	(*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp
+}
+var file_proto_probability_proto_depIdxs = []int32{
+	1, // 0: sentio.Probability.stamps:type_name -> sentio.Probability.Stamp
+	2, // 1: sentio.Probability.Stamp.time:type_name -> google.protobuf.Timestamp
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	2, // [2:2] is the sub-list for extension type_name
+	2, // [2:2] is the sub-list for extension extendee
+	0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_proto_probability_proto_init() }
+func file_proto_probability_proto_init() {
+	if File_proto_probability_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_proto_probability_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Probability); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_proto_probability_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Probability_Stamp); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_proto_probability_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_proto_probability_proto_goTypes,
+		DependencyIndexes: file_proto_probability_proto_depIdxs,
+		MessageInfos:      file_proto_probability_proto_msgTypes,
+	}.Build()
+	File_proto_probability_proto = out.File
+	file_proto_probability_proto_rawDesc = nil
+	file_proto_probability_proto_goTypes = nil
+	file_proto_probability_proto_depIdxs = nil
+}

+ 14 - 0
proto/probability.proto

@@ -0,0 +1,14 @@
+syntax = "proto3";
+package sentio;
+
+option go_package = "./sentio";
+
+import "google/protobuf/timestamp.proto";
+
+message Probability {
+  repeated Stamp stamps = 10 [json_name = "stamps"];
+  message Stamp {
+    google.protobuf.Timestamp time = 1 [json_name = "time"];
+    float value = 2 [json_name = "value"];
+  }
+}

+ 30 - 0
proto/probability_pb2.py

@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# source: proto/probability.proto
+# Protobuf Python Version: 4.25.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17proto/probability.proto\x12\x06sentio\x1a\x1fgoogle/protobuf/timestamp.proto\"\x8f\x01\n\x0bProbability\x12\x31\n\x06stamps\x18\n \x03(\x0b\x32\x19.sentio.Probability.StampR\x06stamps\x1aM\n\x05Stamp\x12.\n\x04time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\x04time\x12\x14\n\x05value\x18\x02 \x01(\x02R\x05valueB\nZ\x08./sentiob\x06proto3')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.probability_pb2', _globals)
+if _descriptor._USE_C_DESCRIPTORS == False:
+  _globals['DESCRIPTOR']._options = None
+  _globals['DESCRIPTOR']._serialized_options = b'Z\010./sentio'
+  _globals['_PROBABILITY']._serialized_start=69
+  _globals['_PROBABILITY']._serialized_end=212
+  _globals['_PROBABILITY_STAMP']._serialized_start=135
+  _globals['_PROBABILITY_STAMP']._serialized_end=212
+# @@protoc_insertion_point(module_scope)

+ 2 - 2
strategy.go

@@ -6,8 +6,8 @@ type Strategy interface {
 	Model() string
 	MarketId() string
 	PositionSymbols() map[Side]string
-	PositionProbabilities() map[Side]float64
+	PositionProbabilities() map[Side]float32
 	Interval() uint8
 
-	Handle(market Market, probability Probability) error
+	Handle(market Market, probability *Probability) error
 }

+ 0 - 144
strategy/alpaca/qqq15_nodelay/strategy.go

@@ -1,144 +0,0 @@
-package main
-
-import (
-	"git.beejay.kim/Gshopper/sentio"
-	"git.beejay.kim/Gshopper/sentio/indicator"
-	"git.beejay.kim/Gshopper/sentio/util"
-)
-
-const (
-	ATR_MULTIPLIER = 3
-	ATR_PERIOD     = 21
-	ATR_HHV        = 10
-)
-
-var Strategy = qqq{}
-
-type qqq struct{}
-
-func (strategy qqq) Name() string {
-	return "Alpaca: QQQ [TQQQ : SQQQ]"
-}
-
-func (strategy qqq) Model() string {
-	return "qqq15"
-}
-
-func (strategy qqq) MarketId() string {
-	return "alpaca"
-}
-
-func (strategy qqq) Interval() uint8 {
-	return 5
-}
-
-func (strategy qqq) PositionSymbols() map[sentio.Side]string {
-	return map[sentio.Side]string{
-		sentio.BASE:  "QQQ",
-		sentio.LONG:  "TQQQ",
-		sentio.SHORT: "SQQQ",
-	}
-}
-
-func (strategy qqq) PositionProbabilities() map[sentio.Side]float64 {
-	return map[sentio.Side]float64{
-		sentio.BASE:  -1,
-		sentio.LONG:  1.0008,
-		sentio.SHORT: .9990,
-	}
-}
-
-func (strategy qqq) Handle(market sentio.Market, probability sentio.Probability) error {
-	var (
-		now        = market.Clock().Now()
-		symbols    = util.Symbols(strategy)
-		stoplosses map[string]float64
-		quotes     map[string]sentio.Quote
-		orders     []sentio.Order
-		err        error
-	)
-
-	// skip too early orders
-	if now.Hour() == 9 && now.Minute() < 45 {
-		return nil
-	}
-
-	// close all orders before market close
-	if now.Hour() == 15 && now.Minute() > 50 {
-		return util.CloseAllOrders(market, strategy)
-	}
-
-	// retrieve running orders
-	if orders, err = market.Orders(sentio.OrderListCriteria{
-		Status:  "open",
-		Symbols: symbols,
-	}); err != nil {
-		return err
-	}
-
-	if stoplosses, err = indicator.AtrTrailingStopLoss(market, 1, ATR_PERIOD, ATR_MULTIPLIER, ATR_HHV, symbols...); err != nil {
-		return err
-	}
-
-	// update stoplosses or close running orders
-	hasClosedOrders := false
-	for i := range orders {
-		if strategy.PositionSymbols()[sentio.LONG] == orders[i].GetSymbol() &&
-			probability.Value < 1.0008 {
-			if _, err = market.CloseOrder(orders[i]); err != nil {
-				return err
-			}
-
-			hasClosedOrders = true
-			continue
-		}
-
-		if strategy.PositionSymbols()[sentio.SHORT] == orders[i].GetSymbol() &&
-			probability.Value > .9998 {
-			if _, err = market.CloseOrder(orders[i]); err != nil {
-				return err
-			}
-
-			hasClosedOrders = true
-			continue
-		}
-
-		if f, ok := stoplosses[orders[i].GetSymbol()]; ok && f > 0 {
-			if err = market.UpdateOrder(orders[i].GetId(), f); err != nil {
-				return err
-			}
-
-			continue
-		}
-	}
-
-	// Prevent new orders if we just closed one
-	if hasClosedOrders {
-		return nil
-	}
-
-	//if probability.TS.Add(time.Minute * time.Duration(int64(strategy.Interval()/2))).Before(now.Round(time.Minute)) {
-	//	return nil
-	//}
-
-	// Prevent BUYs on closing market
-	if now.Hour() == 15 && now.Minute() > 46 {
-		return nil
-	}
-
-	// Prevent Market.CreateOrder while probability is not clear
-	if probability.Value == 1 || probability.Value < 0 {
-		return nil
-	}
-
-	if quotes, err = market.Quotes(symbols...); err != nil {
-		return err
-	}
-
-	var t = sentio.SHORT
-	if probability.Value > 1 {
-		t = sentio.LONG
-	}
-
-	return util.CreateOrder(market, strategy, t, probability, quotes, stoplosses)
-}

+ 19 - 18
strategy/alpaca/qqq15/strategy.go → strategy/alpaca/qqq200/strategy.go

@@ -4,7 +4,6 @@ import (
 	"git.beejay.kim/Gshopper/sentio"
 	"git.beejay.kim/Gshopper/sentio/indicator"
 	"git.beejay.kim/Gshopper/sentio/util"
-	"time"
 )
 
 const (
@@ -22,7 +21,7 @@ func (strategy qqq) Name() string {
 }
 
 func (strategy qqq) Model() string {
-	return "qqq15"
+	return "qqq200"
 }
 
 func (strategy qqq) MarketId() string {
@@ -30,7 +29,7 @@ func (strategy qqq) MarketId() string {
 }
 
 func (strategy qqq) Interval() uint8 {
-	return 5
+	return 1
 }
 
 func (strategy qqq) PositionSymbols() map[sentio.Side]string {
@@ -41,26 +40,28 @@ func (strategy qqq) PositionSymbols() map[sentio.Side]string {
 	}
 }
 
-func (strategy qqq) PositionProbabilities() map[sentio.Side]float64 {
-	return map[sentio.Side]float64{
+func (strategy qqq) PositionProbabilities() map[sentio.Side]float32 {
+	return map[sentio.Side]float32{
 		sentio.BASE:  -1,
-		sentio.LONG:  1.0008,
-		sentio.SHORT: .9990,
+		sentio.LONG:  1.000097,
+		sentio.SHORT: .999867,
 	}
 }
 
-func (strategy qqq) Handle(market sentio.Market, probability sentio.Probability) error {
+func (strategy qqq) Handle(market sentio.Market, probability *sentio.Probability) error {
 	var (
 		now        = market.Clock().Now()
+		stamp      *sentio.Probability_Stamp
 		symbols    = util.Symbols(strategy)
 		stoplosses map[string]float64
 		quotes     map[string]sentio.Quote
 		orders     []sentio.Order
+		ok         bool
 		err        error
 	)
 
 	// skip too early orders
-	if now.Hour() == 9 && now.Minute() < 45 {
+	if now.Hour() == 9 && now.Minute() < 50 {
 		return nil
 	}
 
@@ -69,6 +70,10 @@ func (strategy qqq) Handle(market sentio.Market, probability sentio.Probability)
 		return util.CloseAllOrders(market, strategy)
 	}
 
+	if stamp, ok = probability.First(); !ok {
+		return nil
+	}
+
 	// retrieve running orders
 	if orders, err = market.Orders(sentio.OrderListCriteria{
 		Status:  "open",
@@ -85,7 +90,7 @@ func (strategy qqq) Handle(market sentio.Market, probability sentio.Probability)
 	hasClosedOrders := false
 	for i := range orders {
 		if strategy.PositionSymbols()[sentio.LONG] == orders[i].GetSymbol() &&
-			probability.Value < 1.0008 {
+			stamp.Value < strategy.PositionProbabilities()[sentio.LONG] {
 			if _, err = market.CloseOrder(orders[i]); err != nil {
 				return err
 			}
@@ -95,7 +100,7 @@ func (strategy qqq) Handle(market sentio.Market, probability sentio.Probability)
 		}
 
 		if strategy.PositionSymbols()[sentio.SHORT] == orders[i].GetSymbol() &&
-			probability.Value > .9998 {
+			stamp.Value > strategy.PositionProbabilities()[sentio.SHORT] {
 			if _, err = market.CloseOrder(orders[i]); err != nil {
 				return err
 			}
@@ -114,11 +119,7 @@ func (strategy qqq) Handle(market sentio.Market, probability sentio.Probability)
 	}
 
 	// Prevent new orders if we just closed one
-	if hasClosedOrders {
-		return nil
-	}
-
-	if probability.TS.Add(time.Minute * time.Duration(int64(strategy.Interval()/2))).Before(now.Round(time.Minute)) {
+	if hasClosedOrders || len(orders) > 0 {
 		return nil
 	}
 
@@ -128,7 +129,7 @@ func (strategy qqq) Handle(market sentio.Market, probability sentio.Probability)
 	}
 
 	// Prevent Market.CreateOrder while probability is not clear
-	if probability.Value == 1 || probability.Value < 0 {
+	if stamp.Value == 1 || stamp.Value < 0 {
 		return nil
 	}
 
@@ -137,7 +138,7 @@ func (strategy qqq) Handle(market sentio.Market, probability sentio.Probability)
 	}
 
 	var t = sentio.SHORT
-	if probability.Value > 1 {
+	if stamp.Value > 1 {
 		t = sentio.LONG
 	}
 

+ 17 - 32
util/order.go

@@ -10,17 +10,14 @@ func CreateOrder(
 	m sentio.Market,
 	s sentio.Strategy,
 	t sentio.Side,
-	proba sentio.Probability,
+	probability *sentio.Probability,
 	q map[string]sentio.Quote,
 	sl map[string]float64,
 ) error {
 	var (
 		symbol    string
-		orders    []sentio.Order
 		account   sentio.MarketAccount
-		has       = false
-		avgPrice  float64
-		threshold float64
+		threshold float32
 		size      uint
 		ok        bool
 		err       error
@@ -30,22 +27,6 @@ func CreateOrder(
 		return errors.New("`CreateOrder`: unknown Side for the current strategy")
 	}
 
-	if orders, err = m.Orders(sentio.OrderListCriteria{
-		Status:  "open",
-		Symbols: []string{symbol},
-	}); err != nil {
-		return err
-	}
-
-	has = len(orders) > 0
-	for i := range orders {
-		avgPrice += orders[i].GetEntryPrice()
-	}
-
-	if has {
-		avgPrice = avgPrice / float64(len(orders))
-	}
-
 	// define threshold
 	if threshold, ok = s.PositionProbabilities()[t]; !ok {
 		return nil
@@ -53,7 +34,7 @@ func CreateOrder(
 
 	// Prevent cases when order will be closed ASAP they were opened.
 	// Also, Alpaca requires at least 0.01 gap against base_price
-	if sl[symbol] >= q[symbol].BidPrice-0.011 {
+	if sentio.ToFixed(sl[symbol], 2) > sentio.ToFixed(q[symbol].BidPrice, 2)-0.01 {
 		return sentio.ErrHighStoploss
 	}
 
@@ -65,20 +46,24 @@ func CreateOrder(
 		return sentio.ErrTooSmallOrder
 	}
 
-	if !has && ((sentio.LONG == t && proba.Value > threshold) || (sentio.SHORT == t && proba.Value < threshold)) {
+	var (
+		stamp *sentio.Probability_Stamp
+		next  *sentio.Probability_Stamp
+	)
 
-		// create a new order
-		if size = uint(math.Floor(account.GetCash() * .7 / q[symbol].BidPrice)); size < 1 {
-			return sentio.ErrTooSmallOrder
-		}
+	if stamp, ok = probability.First(); !ok {
+		return nil
+	}
 
-		_, err = m.CreateOrder(symbol, size, sl[symbol])
-		return err
+	if next, ok = probability.Next(1); !ok {
+		return nil
+	}
 
-	} else if has && avgPrice/q[symbol].BidPrice > 1.002 {
+	if (sentio.LONG == t && stamp.Value > threshold && next.Value > threshold) ||
+		(sentio.SHORT == t && stamp.Value < threshold && next.Value < threshold) {
 
-		// create an extra position
-		if size = uint(math.Floor(account.GetCash() * .5 / q[symbol].BidPrice)); size < 1 {
+		// create a new order
+		if size = uint(math.Floor(account.GetCash() * .8 / q[symbol].BidPrice)); size < 1 {
 			return sentio.ErrTooSmallOrder
 		}