Alexey Kim 5 kuukautta sitten
vanhempi
commit
98baf6aef0
6 muutettua tiedostoa jossa 18 lisäystä ja 216 poistoa
  1. 1 0
      errors.go
  2. 0 57
      indicator/art_trailing_stop_loss.go
  3. 1 1
      market.go
  4. 6 0
      risk_manager.go
  5. 0 146
      strategy/alpaca/qqq200/strategy.go
  6. 10 12
      util/order.go

+ 1 - 0
errors.go

@@ -6,4 +6,5 @@ var (
 	ErrMarketClosed  = errors.New("market is closed")
 	ErrTooSmallOrder = errors.New("too small order size")
 	ErrHighStoploss  = errors.New("base price is lower than stoploss")
+	ErrLowTakeProfit = errors.New("base price is too close to take profit")
 )

+ 0 - 57
indicator/art_trailing_stop_loss.go

@@ -1,57 +0,0 @@
-package indicator
-
-import (
-	"errors"
-	"git.beejay.kim/Gshopper/sentio"
-	"git.beejay.kim/Gshopper/sentio/talib"
-	"time"
-)
-
-func AtrTrailingStopLoss(m sentio.Market,
-	period uint,
-	length uint,
-	multiplier float64,
-	hhv int,
-	symbols ...string,
-) (map[string]float64, error) {
-	var (
-		stoplosses map[string]float64
-		bars       map[string][]sentio.Bar
-		err        error
-	)
-
-	if bars, err = m.HistoricalBars(symbols, time.Minute*time.Duration(period), nil); err != nil {
-		return nil, err
-	}
-
-	stoplosses = make(map[string]float64)
-	for s := range bars {
-		if bars == nil || len(bars[s]) < int(length) {
-			return nil, errors.New("AtrStopLoss: could not calculate stoploss for too short timeseries")
-		}
-
-		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
-		}
-
-		atr := talib.Atr(h, l, c, int(length))
-		trailing := make([]float64, len(atr))
-		for i := range atr {
-			trailing[i] = h[i] - multiplier*atr[i]
-		}
-
-		if hhv > 1 {
-			trailing = talib.Max(trailing, hhv)
-		}
-
-		stoplosses[s] = trailing[len(trailing)-1]
-	}
-
-	return stoplosses, nil
-}

+ 1 - 1
market.go

@@ -13,7 +13,7 @@ type Market interface {
 
 	Account() (MarketAccount, error)
 
-	CreateOrder(symbol string, quantity uint, sl float64) (Order, error)
+	CreateOrder(symbol string, quantity uint, rm RiskManager) (Order, error)
 	UpdateOrder(orderID string, sl float64) error
 	CloseOrder(order Order) (Order, error)
 

+ 6 - 0
risk_manager.go

@@ -0,0 +1,6 @@
+package sentio
+
+type RiskManager interface {
+	StopLoss(symbol string) float64
+	TakeProfit(symbol string) float64
+}

+ 0 - 146
strategy/alpaca/qqq200/strategy.go

@@ -1,146 +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 "qqq200"
-}
-
-func (strategy qqq) MarketId() string {
-	return "alpaca"
-}
-
-func (strategy qqq) Interval() uint8 {
-	return 1
-}
-
-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]float32 {
-	return map[sentio.Side]float32{
-		sentio.BASE:  -1,
-		sentio.LONG:  1.000097,
-		sentio.SHORT: .999867,
-	}
-}
-
-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() < 50 {
-		return nil
-	}
-
-	// close all orders before market close
-	if now.Hour() == 15 && now.Minute() > 50 {
-		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",
-		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() &&
-			stamp.Value < strategy.PositionProbabilities()[sentio.LONG] {
-			if _, err = market.CloseOrder(orders[i]); err != nil {
-				return err
-			}
-
-			hasClosedOrders = true
-			continue
-		}
-
-		if strategy.PositionSymbols()[sentio.SHORT] == orders[i].GetSymbol() &&
-			stamp.Value > strategy.PositionProbabilities()[sentio.SHORT] {
-			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 || len(orders) > 0 {
-		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 stamp.Value == 1 || stamp.Value < 0 {
-		return nil
-	}
-
-	if quotes, err = market.Quotes(symbols...); err != nil {
-		return err
-	}
-
-	var t = sentio.SHORT
-	if stamp.Value > 1 {
-		t = sentio.LONG
-	}
-
-	return util.CreateOrder(market, strategy, t, probability, quotes, stoplosses)
-}

+ 10 - 12
util/order.go

@@ -13,11 +13,13 @@ func CreateOrder(
 	probability *sentio.Probability,
 	q map[string]sentio.Quote,
 	sl map[string]float64,
+	tp map[string]float64,
 ) error {
 	var (
 		symbol    string
 		account   sentio.MarketAccount
 		threshold float32
+		stamp     *sentio.Probability_Stamp
 		size      uint
 		ok        bool
 		err       error
@@ -32,6 +34,11 @@ func CreateOrder(
 		return nil
 	}
 
+	// Prevent orders those have too small expected profit
+	if sentio.ToFixed(tp[symbol], 2)/sentio.ToFixed(q[symbol].BidPrice, 2) < 1.0009 {
+		return sentio.ErrLowTakeProfit
+	}
+
 	// Prevent cases when order will be closed ASAP they were opened.
 	// Also, Alpaca requires at least 0.01 gap against base_price
 	if sentio.ToFixed(sl[symbol], 2) > sentio.ToFixed(q[symbol].BidPrice, 2)-0.01 {
@@ -46,28 +53,19 @@ func CreateOrder(
 		return sentio.ErrTooSmallOrder
 	}
 
-	var (
-		stamp *sentio.Probability_Stamp
-		next  *sentio.Probability_Stamp
-	)
-
 	if stamp, ok = probability.First(); !ok {
 		return nil
 	}
 
-	if next, ok = probability.Next(1); !ok {
-		return nil
-	}
-
-	if (sentio.LONG == t && stamp.Value > threshold && next.Value > threshold) ||
-		(sentio.SHORT == t && stamp.Value < threshold && next.Value < threshold) {
+	if sentio.LONG == t && stamp.Value > threshold ||
+		sentio.SHORT == t && stamp.Value < threshold {
 
 		// create a new order
 		if size = uint(math.Floor(account.GetCash() * .8 / q[symbol].BidPrice)); size < 1 {
 			return sentio.ErrTooSmallOrder
 		}
 
-		_, err = m.CreateOrder(symbol, size, sl[symbol])
+		_, err = m.CreateOrder(symbol, size, sl[symbol], tp[symbol])
 		return err
 	}