|
@@ -1,15 +1,16 @@
|
|
package main
|
|
package main
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
+ "errors"
|
|
"git.beejay.kim/Gshopper/sentio"
|
|
"git.beejay.kim/Gshopper/sentio"
|
|
|
|
+ "git.beejay.kim/Gshopper/sentio/indicator"
|
|
|
|
+ "math"
|
|
"time"
|
|
"time"
|
|
)
|
|
)
|
|
|
|
|
|
var Strategy = qqq{}
|
|
var Strategy = qqq{}
|
|
|
|
|
|
-type qqq struct {
|
|
|
|
- sentio.BaseStrategy
|
|
|
|
-}
|
|
|
|
|
|
+type qqq struct{}
|
|
|
|
|
|
func (strategy qqq) Name() string {
|
|
func (strategy qqq) Name() string {
|
|
return "Alpaca: QQQ [TQQQ : SQQQ]"
|
|
return "Alpaca: QQQ [TQQQ : SQQQ]"
|
|
@@ -139,3 +140,136 @@ func (strategy qqq) Handle(market sentio.Market, probability sentio.Probability)
|
|
|
|
|
|
return strategy.CreateOrder(market, t, probability, portfolio, quotes, stoplosses)
|
|
return strategy.CreateOrder(market, t, probability, portfolio, quotes, stoplosses)
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+func (strategy qqq) Symbols() []string {
|
|
|
|
+ var symbols []string
|
|
|
|
+
|
|
|
|
+ for side, s := range strategy.PositionSymbols() {
|
|
|
|
+ if sentio.BASE == side {
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ symbols = append(symbols, s)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return symbols
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (strategy qqq) CloseAllOrders(market sentio.Market) error {
|
|
|
|
+ var (
|
|
|
|
+ symbols = strategy.Symbols()
|
|
|
|
+ orders []sentio.Order
|
|
|
|
+ err error
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ if orders, err = market.Orders(sentio.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 (strategy qqq) AtrStopLoss(market sentio.Market, symbols ...string) (map[string]float64, error) {
|
|
|
|
+ var (
|
|
|
|
+ stoplosses map[string]float64
|
|
|
|
+ bars map[string][]sentio.Bar
|
|
|
|
+ err error
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ if bars, err = market.HistoricalBars(symbols, time.Minute, nil); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if bars == nil || len(bars) < sentio.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, sentio.ATR_PERIOD, sentio.ATR_MULTIPLIER, sentio.ATR_HHV)
|
|
|
|
+ stoplosses[s] = trailing[len(trailing)-1]
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return stoplosses, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (strategy qqq) CreateOrder(m sentio.Market, t sentio.Side, proba sentio.Probability, p sentio.Portfolio, q map[string]sentio.Quote, sl map[string]float64) error {
|
|
|
|
+ var (
|
|
|
|
+ symbol = strategy.PositionSymbols()[t]
|
|
|
|
+ position sentio.Position
|
|
|
|
+ account sentio.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] < sentio.ATR_STOPLOSS_THRESHOLD {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if account, err = m.Account(); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if account.GetCash() < q[symbol].BidPrice {
|
|
|
|
+ return sentio.ErrTooSmallOrder
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if !has && proba.Value > threshold {
|
|
|
|
+
|
|
|
|
+ // create a new order
|
|
|
|
+ if size = uint(math.Floor(account.GetCash() * .7 / q[symbol].BidPrice)); size < 1 {
|
|
|
|
+ return sentio.ErrTooSmallOrder
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _, err = m.CreateOrder(symbol, size, sl[symbol])
|
|
|
|
+ return err
|
|
|
|
+
|
|
|
|
+ } else if has && position.GetAvgPrice()/position.GetCurrentPrice() > sentio.EXTRA_POSITION_THRESHOLD {
|
|
|
|
+
|
|
|
|
+ // create an extra position
|
|
|
|
+ if size = uint(math.Floor(account.GetCash() * .5 / q[symbol].BidPrice)); size < 1 {
|
|
|
|
+ return sentio.ErrTooSmallOrder
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ _, err = m.CreateOrder(symbol, size, sl[symbol])
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return nil
|
|
|
|
+}
|