|
@@ -1,38 +1,25 @@
|
|
-package main
|
|
|
|
|
|
+package qqq15
|
|
|
|
|
|
import (
|
|
import (
|
|
- "fmt"
|
|
|
|
"git.beejay.kim/Gshopper/sentio"
|
|
"git.beejay.kim/Gshopper/sentio"
|
|
- "git.beejay.kim/Gshopper/sentio/indicator"
|
|
|
|
"time"
|
|
"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]"
|
|
return "Alpaca: QQQ [TQQQ : SQQQ]"
|
|
}
|
|
}
|
|
|
|
|
|
-func (s alpacaQQQ) Model() string {
|
|
|
|
|
|
+func (strategy qqq) Model() string {
|
|
return "qqq15"
|
|
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{
|
|
return map[sentio.Side]string{
|
|
sentio.BASE: "QQQ",
|
|
sentio.BASE: "QQQ",
|
|
sentio.LONG: "TQQQ",
|
|
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 (
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
}
|
|
}
|