123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- package sentio
- import (
- "errors"
- "git.beejay.kim/Gshopper/sentio/indicator"
- "math"
- "time"
- )
- type Strategy interface {
- Name() string
- Model() string
- MarketId() string
- PositionSymbols() map[Side]string
- PositionProbabilities() map[Side]float64
- Interval() uint8
- Cooldown(periods uint8) time.Duration
- Handle(market Market, probability Probability) error
- }
- const (
- ATR_MULTIPLIER = 1.5
- ATR_PERIOD = 5
- ATR_HHV = 4
- ATR_STOPLOSS_THRESHOLD = 1
- EXTRA_POSITION_THRESHOLD = 1.002
- )
- type BaseStrategy struct {
- Strategy
- }
- 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 (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 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 nil
- }
|