Alexey Kim 6 hari lalu
induk
melakukan
6380612c94
6 mengubah file dengan 334 tambahan dan 456 penghapusan
  1. 1 1
      market.go
  2. 8 0
      probability.go
  3. 174 12
      strategy.go
  4. 75 160
      strategy/alpaca/qqq15/strategy.go
  5. 76 157
      strategy/alpaca/qqq15_nodelay/strategy.go
  6. 0 126
      strategy_test.go

+ 1 - 1
market.go

@@ -31,7 +31,7 @@ type Market interface {
 	Portfolio() (Portfolio, error)
 	PortfolioHistory() ([]PortfolioRecord, error)
 	Quotes(symbols ...string) (map[string]Quote, error)
-	HistoricalBars(symbol string, interval time.Duration, from *time.Time) ([]Bar, error)
+	HistoricalBars(symbols []string, interval time.Duration, from *time.Time) (map[string][]Bar, error)
 }
 
 type OrderListCriteria struct {

+ 8 - 0
probability.go

@@ -0,0 +1,8 @@
+package sentio
+
+import "time"
+
+type Probability struct {
+	TS    time.Time
+	Value float64
+}

+ 174 - 12
strategy.go

@@ -1,7 +1,9 @@
 package sentio
 
 import (
-	"cmp"
+	"errors"
+	"git.beejay.kim/Gshopper/sentio/indicator"
+	"math"
 	"time"
 )
 
@@ -11,26 +13,186 @@ type Strategy interface {
 	Model() string
 	MarketId() string
 	PositionSymbols() map[Side]string
+	PositionProbabilities() map[Side]float64
 	Interval() uint8
 	Cooldown(periods uint8) time.Duration
 
-	Handle(market Market, ts time.Time, proba float64) ([]StrategyOrder, error)
+	Handle(market Market, probability Probability) error
 }
 
-type StrategyOrder struct {
-	Symbol string      `yaml:"symbol"`
-	Action OrderAction `yaml:"action"`
-	Size   float64     `yaml:"size"`
+const (
+	ATR_MULTIPLIER = 1.5
+	ATR_PERIOD     = 5
+	ATR_HHV        = 4
+
+	ATR_STOPLOSS_THRESHOLD   = 1
+	EXTRA_POSITION_THRESHOLD = 1.002
+)
+
+type BaseStrategy struct {
+}
+
+func (strategy BaseStrategy) Name() string {
+	panic("implement me")
+}
+
+func (strategy BaseStrategy) Model() string {
+	panic("implement me")
+}
+
+func (strategy BaseStrategy) MarketId() string {
+	return "alpaca"
+}
+
+func (strategy BaseStrategy) PositionSymbols() map[Side]string {
+	panic("implement me")
+}
+
+func (strategy BaseStrategy) PositionProbabilities() map[Side]float64 {
+	panic("implement me")
+}
+
+func (strategy BaseStrategy) Interval() uint8 {
+	return 5
+}
+
+func (strategy BaseStrategy) Cooldown(periods uint8) time.Duration {
+	return time.Minute * time.Duration(strategy.Interval()*periods)
+}
+
+func (strategy BaseStrategy) Handle(market Market, probability Probability) error {
+	panic("implement me")
+}
+
+func (strategy BaseStrategy) Symbols() []string {
+	var symbols []string
+
+	for side, s := range strategy.PositionSymbols() {
+		if BASE == side {
+			continue
+		}
+
+		symbols = append(symbols, s)
+	}
+
+	return symbols
+}
+
+func (strategy BaseStrategy) CloseAllOrders(market Market) error {
+	var (
+		symbols = strategy.Symbols()
+		orders  []Order
+		err     error
+	)
+
+	if orders, err = market.Orders(OrderListCriteria{
+		Status:  "open",
+		Symbols: symbols,
+		Nested:  true,
+	}); err != nil {
+		return err
+	}
+
+	for i := range orders {
+		if _, err = market.CloseOrder(orders[i].GetId()); err != nil {
+			return err
+		}
+	}
+
+	return nil
 }
 
-func CompareStrategyOrders(a, b StrategyOrder) int {
-	if a.Action == b.Action {
-		return cmp.Compare(b.Size, a.Size)
+func (strategy BaseStrategy) AtrStopLoss(market Market, symbols ...string) (map[string]float64, error) {
+	var (
+		stoplosses map[string]float64
+		bars       map[string][]Bar
+		err        error
+	)
+
+	if bars, err = market.HistoricalBars(symbols, time.Minute, nil); err != nil {
+		return nil, err
 	}
 
-	if OrderSell == a.Action {
-		return -1
+	if bars == nil || len(bars) < ATR_PERIOD {
+		return nil, errors.New("AtrStopLoss: could not calculate stoploss for too short timeseries")
+	}
+
+	stoplosses = make(map[string]float64)
+	for s := range bars {
+		h := make([]float64, len(bars[s]))
+		l := make([]float64, len(bars[s]))
+		c := make([]float64, len(bars[s]))
+
+		for i := range bars[s] {
+			h[i] = bars[s][i].High
+			l[i] = bars[s][i].Low
+			c[i] = bars[s][i].Close
+		}
+
+		trailing := indicator.AtrTrailingStopLoss(h, l, c, ATR_PERIOD, ATR_MULTIPLIER, ATR_HHV)
+		stoplosses[s] = trailing[len(trailing)-1]
+	}
+
+	return stoplosses, nil
+}
+
+func (strategy BaseStrategy) CreateOrder(m Market, t Side, proba Probability, p Portfolio, q map[string]Quote, sl map[string]float64) error {
+	var (
+		symbol    = strategy.PositionSymbols()[t]
+		position  Position
+		account   MarketAccount
+		has       = false
+		threshold float64
+		size      uint
+		ok        bool
+		err       error
+	)
+
+	// ensure portfolio
+	position, has = p.Get(symbol)
+
+	// define threshold
+	if threshold, ok = strategy.PositionProbabilities()[t]; !ok {
+		threshold = -1
+	}
+
+	if threshold == -1 || symbol == "" {
+		return nil
+	}
+
+	// Prevent Market.CreateOrder when BidPrice less than ATR_STOPLOSS_THRESHOLD
+	if q[symbol].BidPrice/sl[symbol] < ATR_STOPLOSS_THRESHOLD {
+		return nil
+	}
+
+	if account, err = m.Account(); err != nil {
+		return err
+	}
+
+	if account.GetCash() < q[symbol].BidPrice {
+		return ErrTooSmallOrder
+	}
+
+	if !has && proba.Value > threshold {
+
+		// create a new order
+		if size = uint(math.Floor(account.GetCash() * .7 / q[symbol].BidPrice)); size < 1 {
+			return ErrTooSmallOrder
+		}
+
+		_, err = m.CreateOrder(symbol, size, sl[symbol])
+		return err
+
+	} else if has && position.GetAvgPrice()/position.GetCurrentPrice() > EXTRA_POSITION_THRESHOLD {
+
+		// create an extra position
+		if size = uint(math.Floor(account.GetCash() * .5 / q[symbol].BidPrice)); size < 1 {
+			return ErrTooSmallOrder
+		}
+
+		_, err = m.CreateOrder(symbol, size, sl[symbol])
+		return err
 	}
 
-	return 1
+	return nil
 }

+ 75 - 160
strategy/alpaca/qqq15/strategy.go

@@ -1,38 +1,25 @@
-package main
+package qqq15
 
 import (
-	"fmt"
 	"git.beejay.kim/Gshopper/sentio"
-	"git.beejay.kim/Gshopper/sentio/indicator"
 	"time"
 )
 
-const (
-	ATR_MULTIPLIER         = 1.5
-	ATR_PERIOD             = 5
-	ATR_HHV                = 4
-	ATR_STOPLOSS_THRESHOLD = 1
+var Strategy = qqq{}
 
-	EXTRA_POSITION_THRESHOLD = 1.002
-)
-
-var Strategy = alpacaQQQ{}
-
-type alpacaQQQ struct{}
+type qqq struct {
+	sentio.BaseStrategy
+}
 
-func (s alpacaQQQ) Name() string {
+func (strategy qqq) Name() string {
 	return "Alpaca: QQQ [TQQQ : SQQQ]"
 }
 
-func (s alpacaQQQ) Model() string {
+func (strategy qqq) Model() string {
 	return "qqq15"
 }
 
-func (s alpacaQQQ) MarketId() string {
-	return "alpaca"
-}
-
-func (s alpacaQQQ) PositionSymbols() map[sentio.Side]string {
+func (strategy qqq) PositionSymbols() map[sentio.Side]string {
 	return map[sentio.Side]string{
 		sentio.BASE:  "QQQ",
 		sentio.LONG:  "TQQQ",
@@ -40,175 +27,103 @@ func (s alpacaQQQ) PositionSymbols() map[sentio.Side]string {
 	}
 }
 
-func (s alpacaQQQ) Interval() uint8 {
-	return 5
-}
-
-func (s alpacaQQQ) Cooldown(periods uint8) time.Duration {
-	return time.Minute * time.Duration(s.Interval()*periods)
+func (strategy qqq) PositionProbabilities() map[sentio.Side]float64 {
+	return map[sentio.Side]float64{
+		sentio.BASE:  -1,
+		sentio.LONG:  1.0008,
+		sentio.SHORT: .9990,
+	}
 }
 
-func (s alpacaQQQ) Handle(market sentio.Market, ts time.Time, proba float64) ([]sentio.StrategyOrder, error) {
+func (strategy qqq) Handle(market sentio.Market, probability sentio.Probability) error {
 	var (
-		portfolio sentio.Portfolio
-		orders    []sentio.StrategyOrder
-		now       = market.Time().Now()
-		err       error
+		now        = market.Clock().Now()
+		symbols    = strategy.Symbols()
+		stoplosses map[string]float64
+		quotes     map[string]sentio.Quote
+		orders     []sentio.Order
+		portfolio  sentio.Portfolio
+		err        error
 	)
 
-	// skip too early trades
+	// skip too early orders
 	if now.Hour() == 9 && now.Minute() < 45 {
-		return orders, nil
+		return nil
 	}
 
-	if portfolio, err = market.Portfolio(); err != nil {
-		return nil, err
+	// close all orders before market close
+	if now.Hour() == 15 && now.Minute() > 50 {
+		return strategy.CloseAllOrders(market)
 	}
 
-	for side, symbol := range s.PositionSymbols() {
-		var (
-			position   sentio.Position
-			quote      *sentio.Quote
-			bars       []sentio.Bar
-			stoploss   float64
-			uptrending bool
-			ok         bool
-		)
-
-		// no need to trade BASE quote
-		if sentio.BASE == side {
-			continue
-		}
-
-		if quote, err = market.Quote(symbol); err != nil {
-			return nil, err
-		}
-
-		fmt.Printf("Quotes: %s [BID: %f] [ASK: %s]\n", symbol, quote.BidPrice, quote.AskPrice)
+	// retrieve running orders
+	if orders, err = market.Orders(sentio.OrderListCriteria{
+		Status:  "open",
+		Symbols: symbols,
+		Nested:  true,
+	}); err != nil {
+		return err
+	}
 
-		if bars, err = market.HistoricalBars(symbol, time.Minute, nil); err == nil && len(bars) > 0 {
-			h := make([]float64, len(bars))
-			l := make([]float64, len(bars))
-			c := make([]float64, len(bars))
+	if stoplosses, err = strategy.AtrStopLoss(market, symbols...); err != nil {
+		return err
+	}
 
-			for i := range bars {
-				h[i] = bars[i].High
-				l[i] = bars[i].Low
-				c[i] = bars[i].Close
+	// update stoplosses or close running orders
+	for i := range orders {
+		if strategy.PositionSymbols()[sentio.LONG] == orders[i].GetSymbol() &&
+			probability.Value < 1.0008 {
+			if _, err = market.CloseOrder(orders[i].GetId()); err != nil {
+				return err
 			}
 
-			trailing := indicator.AtrTrailingStopLoss(h, l, c, ATR_PERIOD, ATR_MULTIPLIER, ATR_HHV)
-			stoploss = trailing[len(trailing)-1]
-			uptrending = trailing[len(trailing)-1] > trailing[len(trailing)-2]
-
-			fmt.Printf("ATR Trailing StopLoss: [%s: %f; uptrading: %v]\n", symbol, stoploss, uptrending)
-		}
-
-		if portfolio != nil {
-			position, ok = portfolio.Get(symbol)
-			ok = ok && position != nil && position.GetSize() != 0
-		} else {
-			ok = false
-		}
-
-		// Close positions before market closed
-		if ok && now.Hour() == 15 && now.Minute() > 55 {
-			return []sentio.StrategyOrder{{
-				Symbol: symbol,
-				Action: sentio.OrderSell,
-				Ratio:  1,
-			}}, nil
-		}
-
-		// Close position if BidPrice less than StopLoss
-		if ok && !uptrending && quote.BidPrice/stoploss < ATR_STOPLOSS_THRESHOLD {
-			return []sentio.StrategyOrder{{
-				Symbol: symbol,
-				Action: sentio.OrderSell,
-				Ratio:  1,
-			}}, nil
-		}
-
-		if proba < 0 {
 			continue
 		}
 
-		// Close LONG position
-		if ok && sentio.LONG == side && proba < 1.0008 {
-			return []sentio.StrategyOrder{{
-				Symbol: symbol,
-				Action: sentio.OrderSell,
-				Ratio:  1,
-			}}, nil
-		}
-
-		// Close SHORT position
-		if ok && sentio.SHORT == side && proba > .9998 {
-			return []sentio.StrategyOrder{{
-				Symbol: symbol,
-				Action: sentio.OrderSell,
-				Ratio:  1,
-			}}, nil
-		}
+		if strategy.PositionSymbols()[sentio.SHORT] == orders[i].GetSymbol() &&
+			probability.Value > .9998 {
+			if _, err = market.CloseOrder(orders[i].GetId()); err != nil {
+				return err
+			}
 
-		// Prevent BUYs for delayed probas
-		if ts.Add(time.Minute * time.Duration(int64(s.Interval()/2))).Before(now.Round(time.Minute)) {
 			continue
 		}
 
-		// Prevent BUYs on closing market
-		if now.Hour() == 15 && now.Minute() > 46 {
-			continue
-		}
+		if f, ok := stoplosses[orders[i].GetId()]; ok && f > 0 {
+			if err = market.UpdateOrder(orders[i].GetId(), f); err != nil {
+				return err
+			}
 
-		if quote.BidPrice/stoploss < ATR_STOPLOSS_THRESHOLD {
 			continue
 		}
+	}
 
-		if sentio.LONG == side && proba > 1 {
-			size := float64(0)
-
-			if ok && position.GetAvgPrice()/position.GetCurrentPrice() > EXTRA_POSITION_THRESHOLD {
-				// extra position with cheaper price
-				size = .5
-			} else if !ok && proba > 1.0008 {
-				// new trade position
-				size = .7
-			}
-
-			if size > 0 {
-				orders = append(orders, sentio.StrategyOrder{
-					Symbol: symbol,
-					Action: sentio.OrderBuy,
-					Ratio:  size,
-				})
-			}
+	if probability.TS.Add(time.Minute * time.Duration(int64(strategy.Interval()/2))).Before(now.Round(time.Minute)) {
+		return nil
+	}
 
-			continue
-		}
+	// Prevent BUYs on closing market
+	if now.Hour() == 15 && now.Minute() > 46 {
+		return nil
+	}
 
-		if sentio.SHORT == side && proba < 1 {
-			size := float64(0)
+	// Prevent Market.CreateOrder while probability is not clear
+	if probability.Value == 1 || probability.Value < 0 {
+		return nil
+	}
 
-			if ok && position.GetAvgPrice()/position.GetCurrentPrice() > EXTRA_POSITION_THRESHOLD {
-				// extra position with cheaper price
-				size = .5
-			} else if !ok && proba < .9990 {
-				// new trade position
-				size = .7
-			}
+	if portfolio, err = market.Portfolio(); err != nil {
+		return err
+	}
 
-			if size > 0 {
-				orders = append(orders, sentio.StrategyOrder{
-					Symbol: symbol,
-					Action: sentio.OrderBuy,
-					Ratio:  size,
-				})
-			}
+	if quotes, err = market.Quotes(symbols...); err != nil {
+		return err
+	}
 
-			continue
-		}
+	var t = sentio.SHORT
+	if probability.Value > 1 {
+		t = sentio.LONG
 	}
 
-	return orders, nil
+	return strategy.CreateOrder(market, t, probability, portfolio, quotes, stoplosses)
 }

+ 76 - 157
strategy/alpaca/qqq15_nodelay/strategy.go

@@ -1,38 +1,24 @@
-package main
+package qqq15_nodelay
 
 import (
-	"fmt"
 	"git.beejay.kim/Gshopper/sentio"
-	"git.beejay.kim/Gshopper/sentio/indicator"
-	"time"
 )
 
-const (
-	ATR_MULTIPLIER         = 1.5
-	ATR_PERIOD             = 5
-	ATR_HHV                = 4
-	ATR_STOPLOSS_THRESHOLD = 1
+var Strategy = qqq{}
 
-	EXTRA_POSITION_THRESHOLD = 1.002
-)
-
-var Strategy = alpacaQQQ{}
-
-type alpacaQQQ struct{}
+type qqq struct {
+	sentio.BaseStrategy
+}
 
-func (s alpacaQQQ) Name() string {
+func (strategy qqq) Name() string {
 	return "Alpaca: QQQ [TQQQ : SQQQ]"
 }
 
-func (s alpacaQQQ) Model() string {
+func (strategy qqq) Model() string {
 	return "qqq15"
 }
 
-func (s alpacaQQQ) MarketId() string {
-	return "alpaca"
-}
-
-func (s alpacaQQQ) PositionSymbols() map[sentio.Side]string {
+func (strategy qqq) PositionSymbols() map[sentio.Side]string {
 	return map[sentio.Side]string{
 		sentio.BASE:  "QQQ",
 		sentio.LONG:  "TQQQ",
@@ -40,170 +26,103 @@ func (s alpacaQQQ) PositionSymbols() map[sentio.Side]string {
 	}
 }
 
-func (s alpacaQQQ) Interval() uint8 {
-	return 5
-}
-
-func (s alpacaQQQ) Cooldown(periods uint8) time.Duration {
-	return time.Minute * time.Duration(s.Interval()*periods)
+func (strategy qqq) PositionProbabilities() map[sentio.Side]float64 {
+	return map[sentio.Side]float64{
+		sentio.BASE:  -1,
+		sentio.LONG:  1.0008,
+		sentio.SHORT: .9990,
+	}
 }
 
-func (s alpacaQQQ) Handle(market sentio.Market, ts time.Time, proba float64) ([]sentio.StrategyOrder, error) {
+func (strategy qqq) Handle(market sentio.Market, probability sentio.Probability) error {
 	var (
-		portfolio sentio.Portfolio
-		orders    []sentio.StrategyOrder
-		now       = market.Time().Now()
-		err       error
+		now        = market.Clock().Now()
+		symbols    = strategy.Symbols()
+		stoplosses map[string]float64
+		quotes     map[string]sentio.Quote
+		orders     []sentio.Order
+		portfolio  sentio.Portfolio
+		err        error
 	)
 
-	// skip too early trades
+	// skip too early orders
 	if now.Hour() == 9 && now.Minute() < 45 {
-		return orders, nil
+		return nil
 	}
 
-	if portfolio, err = market.Portfolio(); err != nil {
-		return nil, err
+	// close all orders before market close
+	if now.Hour() == 15 && now.Minute() > 50 {
+		return strategy.CloseAllOrders(market)
 	}
 
-	for side, symbol := range s.PositionSymbols() {
-		var (
-			position   sentio.Position
-			quote      *sentio.Quote
-			bars       []sentio.Bar
-			stoploss   float64
-			uptrending bool
-			ok         bool
-		)
-
-		// no need to trade BASE quote
-		if sentio.BASE == side {
-			continue
-		}
-
-		if quote, err = market.Quote(symbol); err != nil {
-			return nil, err
-		}
-
-		fmt.Printf("Quotes: %s [BID: %f] [ASK: %s]\n", symbol, quote.BidPrice, quote.AskPrice)
+	// retrieve running orders
+	if orders, err = market.Orders(sentio.OrderListCriteria{
+		Status:  "open",
+		Symbols: symbols,
+		Nested:  true,
+	}); err != nil {
+		return err
+	}
 
-		if bars, err = market.HistoricalBars(symbol, time.Minute, nil); err == nil && len(bars) > 0 {
-			h := make([]float64, len(bars))
-			l := make([]float64, len(bars))
-			c := make([]float64, len(bars))
+	if stoplosses, err = strategy.AtrStopLoss(market, symbols...); err != nil {
+		return err
+	}
 
-			for i := range bars {
-				h[i] = bars[i].High
-				l[i] = bars[i].Low
-				c[i] = bars[i].Close
+	// update stoplosses or close running orders
+	for i := range orders {
+		if strategy.PositionSymbols()[sentio.LONG] == orders[i].GetSymbol() &&
+			probability.Value < 1.0008 {
+			if _, err = market.CloseOrder(orders[i].GetId()); err != nil {
+				return err
 			}
 
-			trailing := indicator.AtrTrailingStopLoss(h, l, c, ATR_PERIOD, ATR_MULTIPLIER, ATR_HHV)
-			stoploss = trailing[len(trailing)-1]
-			uptrending = trailing[len(trailing)-1] > trailing[len(trailing)-2]
-
-			fmt.Printf("ATR Trailing StopLoss: [%s: %f; uptrading: %v]\n", symbol, stoploss, uptrending)
-		}
-
-		if portfolio != nil {
-			position, ok = portfolio.Get(symbol)
-			ok = ok && position != nil && position.GetSize() != 0
-		} else {
-			ok = false
-		}
-
-		// Close positions before market closed
-		if ok && now.Hour() == 15 && now.Minute() > 55 {
-			return []sentio.StrategyOrder{{
-				Symbol: symbol,
-				Action: sentio.OrderSell,
-				Ratio:  1,
-			}}, nil
-		}
-
-		// Close position if BidPrice less than StopLoss
-		if ok && !uptrending && quote.BidPrice/stoploss < ATR_STOPLOSS_THRESHOLD {
-			return []sentio.StrategyOrder{{
-				Symbol: symbol,
-				Action: sentio.OrderSell,
-				Ratio:  1,
-			}}, nil
-		}
-
-		if proba < 0 {
 			continue
 		}
 
-		// Close LONG position
-		if ok && sentio.LONG == side && proba < 1.0008 {
-			return []sentio.StrategyOrder{{
-				Symbol: symbol,
-				Action: sentio.OrderSell,
-				Ratio:  1,
-			}}, nil
-		}
-
-		// Close SHORT position
-		if ok && sentio.SHORT == side && proba > .9998 {
-			return []sentio.StrategyOrder{{
-				Symbol: symbol,
-				Action: sentio.OrderSell,
-				Ratio:  1,
-			}}, nil
-		}
-
-		// Prevent BUYs on closing market
-		if now.Hour() == 15 && now.Minute() > 46 {
-			continue
-		}
+		if strategy.PositionSymbols()[sentio.SHORT] == orders[i].GetSymbol() &&
+			probability.Value > .9998 {
+			if _, err = market.CloseOrder(orders[i].GetId()); err != nil {
+				return err
+			}
 
-		if quote.BidPrice/stoploss < ATR_STOPLOSS_THRESHOLD {
 			continue
 		}
 
-		if sentio.LONG == side && proba > 1 {
-			size := float64(0)
-
-			if ok && position.GetAvgPrice()/position.GetCurrentPrice() > EXTRA_POSITION_THRESHOLD {
-				// extra position with cheaper price
-				size = .5
-			} else if !ok && proba > 1.0008 {
-				// new trade position
-				size = .7
-			}
-
-			if size > 0 {
-				orders = append(orders, sentio.StrategyOrder{
-					Symbol: symbol,
-					Action: sentio.OrderBuy,
-					Ratio:  size,
-				})
+		if f, ok := stoplosses[orders[i].GetId()]; ok && f > 0 {
+			if err = market.UpdateOrder(orders[i].GetId(), f); err != nil {
+				return err
 			}
 
 			continue
 		}
+	}
 
-		if sentio.SHORT == side && proba < 1 {
-			size := float64(0)
+	//if probability.TS.Add(time.Minute * time.Duration(int64(strategy.Interval()/2))).Before(now.Round(time.Minute)) {
+	//	return nil
+	//}
 
-			if ok && position.GetAvgPrice()/position.GetCurrentPrice() > EXTRA_POSITION_THRESHOLD {
-				// extra position with cheaper price
-				size = .5
-			} else if !ok && proba < .9990 {
-				// new trade position
-				size = .7
-			}
+	// Prevent BUYs on closing market
+	if now.Hour() == 15 && now.Minute() > 46 {
+		return nil
+	}
 
-			if size > 0 {
-				orders = append(orders, sentio.StrategyOrder{
-					Symbol: symbol,
-					Action: sentio.OrderBuy,
-					Ratio:  size,
-				})
-			}
+	// Prevent Market.CreateOrder while probability is not clear
+	if probability.Value == 1 || probability.Value < 0 {
+		return nil
+	}
 
-			continue
-		}
+	if portfolio, err = market.Portfolio(); err != nil {
+		return err
+	}
+
+	if quotes, err = market.Quotes(symbols...); err != nil {
+		return err
+	}
+
+	var t = sentio.SHORT
+	if probability.Value > 1 {
+		t = sentio.LONG
 	}
 
-	return orders, nil
+	return strategy.CreateOrder(market, t, probability, portfolio, quotes, stoplosses)
 }

+ 0 - 126
strategy_test.go

@@ -1,126 +0,0 @@
-package sentio
-
-import (
-	"reflect"
-	"slices"
-	"testing"
-)
-
-func TestCompareStrategyOrders(t *testing.T) {
-	tests := []struct {
-		name   string
-		orders []StrategyOrder
-		want   []StrategyOrder
-	}{
-		{
-			name: "Sell first",
-			orders: []StrategyOrder{
-				{
-					Action: OrderBuy,
-					Ratio:  .3,
-				},
-				{
-					Action: OrderSell,
-					Ratio:  1,
-				},
-			},
-			want: []StrategyOrder{
-				{
-					Action: OrderSell,
-					Ratio:  1,
-				},
-				{
-					Action: OrderBuy,
-					Ratio:  .3,
-				},
-			},
-		},
-		{
-			name: "Buy largest first",
-			orders: []StrategyOrder{
-				{
-					Action: OrderBuy,
-					Ratio:  .3,
-				},
-				{
-					Action: OrderBuy,
-					Ratio:  .6,
-				},
-			},
-			want: []StrategyOrder{
-				{
-					Action: OrderBuy,
-					Ratio:  .6,
-				},
-				{
-					Action: OrderBuy,
-					Ratio:  .3,
-				},
-			},
-		},
-		{
-			name: "Sell largest first",
-			orders: []StrategyOrder{
-				{
-					Action: OrderSell,
-					Ratio:  .3,
-				},
-				{
-					Action: OrderSell,
-					Ratio:  .6,
-				},
-			},
-			want: []StrategyOrder{
-				{
-					Action: OrderSell,
-					Ratio:  .6,
-				},
-				{
-					Action: OrderSell,
-					Ratio:  .3,
-				},
-			},
-		},
-		{
-			name: "Sell largest first then buy",
-			orders: []StrategyOrder{
-				{
-					Action: OrderSell,
-					Ratio:  .3,
-				},
-				{
-					Action: OrderBuy,
-					Ratio:  .6,
-				},
-				{
-					Action: OrderSell,
-					Ratio:  .6,
-				},
-			},
-			want: []StrategyOrder{
-				{
-					Action: OrderSell,
-					Ratio:  .6,
-				},
-				{
-					Action: OrderSell,
-					Ratio:  .3,
-				},
-				{
-					Action: OrderBuy,
-					Ratio:  .6,
-				},
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			slices.SortFunc(tt.orders, CompareStrategyOrders)
-
-			if ok := reflect.DeepEqual(tt.want, tt.orders); !ok {
-				t.Errorf("SortFunc = %v, want %v", tt.orders, tt.want)
-			}
-		})
-	}
-}