We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle cancel only mode from Kraken
sats-stacker/exchange/kraken.go
Line 200 in db77283
"github.com/beldur/kraken-go-api-client" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" "math/big" "reflect" "strconv" "strings" //"time" ) type Kraken struct { Name string Action string ApiKey string SecretKey string Crypto string Fiat string Api *krakenapi.KrakenApi Pair string BalanceCrypto float64 BalanceFiat float64 Ticker krakenapi.PairTickerInfo Ask string AskFloat float64 UserRef int32 } const MIN_BTC_AMOUNT = 0.0002 //func getTimeInfo() (time.Time, time.Time, time.Duration) { // // Always use the local timezone // loc, _ := time.LoadLocation("Local") // // now := time.Now().In(loc) // year, month, day := now.Date() // // // Start is TODAY at 00:00 // start := time.Date(year, month, day, 0, 0, 0, 0, now.Location()) // // // END is now // end := now // // return start, end, end.Sub(start) //} func (k *Kraken) Config(c *cli.Context) error { k.Name = strings.ToTitle("kraken") k.ApiKey = c.String("api-key") k.SecretKey = c.String("secret-key") k.Crypto = "XBT" return nil } func (k *Kraken) Init(c *cli.Context) error { k.Fiat = strings.ToUpper(c.String("fiat")) k.Pair = "X" + k.Crypto + "Z" + k.Fiat k.Api = krakenapi.New(k.ApiKey, k.SecretKey) k.Action = c.Command.FullName() if k.Action != "withdraw" { // Initialize the current Balance balance, err := k.Api.Balance() if err != nil { return errors.New("Failed to get Balance. Check API and SECRET Keys") } // Extract Values from Kraken Responses refBalance := reflect.ValueOf(balance) k.BalanceCrypto = reflect.Indirect(refBalance).FieldByName("X" + k.Crypto).Interface().(float64) k.BalanceFiat = reflect.Indirect(refBalance).FieldByName("Z" + k.Fiat).Interface().(float64) // Get the current ticker for the given PAIR ticker, err := k.Api.Ticker(k.Pair) if err != nil { return fmt.Errorf("Failed to get ticker for pair %s: %s", k.Pair, err) } k.Ticker = ticker.GetPairTickerInfo(k.Pair) k.Ask = ticker.GetPairTickerInfo(k.Pair).Ask[0] k.AskFloat, err = strconv.ParseFloat(k.Ask, 64) if err != nil { return fmt.Errorf("Failed to get Ask price for pair %s: %s", k.Pair, err) } } return nil } //func (k *Kraken) closedOrderTodayForUserRef(orders *krakenapi.ClosedOrdersResponse, c *cli.Context) (string, krakenapi.Order, error) { // // for id, v := range orders.Closed { // if v.Status == "closed" { // return id, v, nil // } // } // return "", krakenapi.Order{}, nil //} func (k *Kraken) priceModifierBasedOnGapFromHighPrice(c *cli.Context) (float64, error) { // 15 interval will give a week worth of data ohlcs, err := k.Api.OHLCWithInterval(k.Pair, "15") if err != nil { return 0.0, fmt.Errorf("Failed to get OHLC Data for pair %s: %s", k.Pair, err) } // Find highest price in the range of OHLC var highest float64 for _, o := range ohlcs.OHLC { if o.High > highest { highest = o.High } } // max modifier is 40% ( applied to the discount price ) when the gap is >= 25% maxDiscountModifier := 40.0 var discountModifier float64 // Is the highest price from the last week more than GAP PERCENTAGE over the current ask price ? gapPrice := highest - k.AskFloat if gapPrice > 0 { gapPercentage := gapPrice / highest * 100 if gapPercentage > c.Float64("high-price-gap-percentage") { // calculate modifier discountModifier = gapPercentage / 25.0 * maxDiscountModifier if discountModifier > maxDiscountModifier { discountModifier = maxDiscountModifier } log.WithFields(logrus.Fields{ "action": k.Action, "pair": k.Pair, "arg-gap-interval": "7d", "arg-gap-percentage": c.Float64("high-price-gap-percentage"), "highest": highest, "ask": k.AskFloat, "gap": gapPrice, "gap-percentage": gapPercentage, "discount-modifier": discountModifier, }).Debug("Price Gap calculator") } } return discountModifier, nil } func (k *Kraken) createOrderArgs(c *cli.Context, volume float64, price string, longshot bool) (map[string]string, error) { args := make(map[string]string) // Simple DCA if k.Action == "stack" { args["orderType"] = "market" } else if k.Action == "btd" { args["orderType"] = "limit" } else { return args, fmt.Errorf("Unknown Action: %s", k.Action) } validate := "false" if c.Bool("dry-run") { validate = "true" args["validate"] = "true" } args["userref"] = fmt.Sprintf("%d", k.UserRef) args["volume"] = strconv.FormatFloat(volume, 'f', 8, 64) args["price"] = price args["oflags"] = "fciq" // "buy" button will actually sell the quote currency in exchange for the base currency, pay fee in the the quote currenty ( fiat ) // If volume < MIN_BTC_AMOUNT then error - this is the minimum kraken order volume if volume < MIN_BTC_AMOUNT { return args, fmt.Errorf("Minimum volume for BTC Order on Kraken is %f got %s. Consider increasing the amount of Fiat", MIN_BTC_AMOUNT, args["volume"]) } log.WithFields(logrus.Fields{ "action": k.Action, "pair": k.Pair, "type": "buy", "orderType": args["orderType"], "volume": args["volume"], "price": args["price"], "dryrun": validate, "orderFlags": args["oflags"], "userref": args["userref"], }).Debug("Order to execute") return args, nil } func (k *Kraken) BuyTheDips(c *cli.Context) (result string, e error) { // TODO Handle cancel only mode from Kraken // TODO Add modifier to customize drift from high price k.UserRef = 300 log.WithFields(logrus.Fields{ "action": "btd", "userRef": k.UserRef, }).Info("Buying the DIPs on " + k.Name) log.WithFields(logrus.Fields{ "action": "btd", "crypto": k.Crypto, "cryptoBalance": k.BalanceCrypto, "fiat": k.Fiat, "fiatBalance": k.BalanceFiat, "ask": k.Ask, "budget": c.Float64("budget"), "n-orders": c.Int64("n-orders"), }).Debug("Balance before any action is taken") // Calculate order values from budget // Each _Unit_ will have the double the value of the unit before var totalOrderUnits int64 var fiatValueUnit float64 totalOrders := c.Int64("n-orders") for totalOrders != 0 { totalOrderUnits += totalOrders totalOrders -= 1 } fiatValueUnit = c.Float64("budget") / float64(totalOrderUnits) //var dipOrders []map[string]string log.WithFields(logrus.Fields{ "action": "btd", "budget": c.Float64("budget"), "total-sats": fmt.Sprintf("%.8f", c.Float64("budget")/k.AskFloat), "dip-percentage": c.Int64("dip-percentage"), "dip-increments": c.Int64("dip-increments"), "n-orders": c.Int64("n-orders"), "total-units": totalOrderUnits, "fiat-value-unit": fiatValueUnit, }).Debug("Calculating orders") var dipOrders []map[string]string var orderNumber int64 //Calculate DIP Discount for this order modifier, err := k.priceModifierBasedOnGapFromHighPrice(c) if err != nil { modifier = 0.0 } for orderNumber != c.Int64("n-orders") { // Discount based on order number discount := float64(c.Int64("dip-percentage") + (orderNumber * c.Int64("dip-increments"))) // Calculate modifier to apply to discount based on the gap from the Highest Weekly price discountModifier := (float64(discount) * modifier) / float64(100.0) dipDiscountedPrice := (k.AskFloat / float64(100)) * (float64(100.0) - discount + discountModifier) dipVolume := (fiatValueUnit * float64(orderNumber+1)) / dipDiscountedPrice log.WithFields(logrus.Fields{ "action": "btd", "order-number": orderNumber + 1, "ask-price": k.Ask, "dip-discount": discount - discountModifier, "dip-price": dipDiscountedPrice, "dip-volume": dipVolume, }).Debug(fmt.Sprintf("Creating discounted order %d", orderNumber+1)) // Create Order and add to list dipOrderArgs, _ := k.createOrderArgs(c, dipVolume, fmt.Sprintf("%.1f", dipDiscountedPrice), false) // If volume < MIN_BTC_AMOUNT then do not add to the list, skip to next iteration if dipVolume < MIN_BTC_AMOUNT { orderNumber += 1 continue } dipOrders = append(dipOrders, dipOrderArgs) log.WithFields(logrus.Fields{ "action": "btd", "order-number": orderNumber + 1, }).Debug("Added Order to list") orderNumber += 1 } if len(dipOrders) == 0 { return "", fmt.Errorf("No Orders were added to the list") } //Cancel any open order with our UserRef if !c.Bool("dry-run") { openordersArgs := make(map[string]string) //openordersArgs["trades"] = "true" openordersArgs["userref"] = fmt.Sprintf("%d", k.UserRef) resp, _ := k.Api.OpenOrders(openordersArgs) if len(resp.Open) > 0 { _, err := k.Api.CancelOrder(fmt.Sprintf("%d", k.UserRef)) if err != nil { return "", fmt.Errorf("Failed to Cancel Orders for UserRef: %d - %s", k.UserRef, err) } log.WithFields(logrus.Fields{ "action": "btd", "userref": k.UserRef, }).Info(fmt.Sprintf("%d Open Orders Canceled", len(resp.Open))) } } //Place Orders orderNumber = 0 for orderNumber != int64(len(dipOrders)) { thisOrder := dipOrders[orderNumber] order, err := k.Api.AddOrder(k.Pair, "buy", thisOrder["orderType"], thisOrder["volume"], thisOrder) if err != nil { log.WithFields(logrus.Fields{ "action": "btd", "userref": thisOrder["userref"], "order-number": orderNumber + 1, }).Error(fmt.Sprintf("Error Creating orderNumber %d: %s", orderNumber+1, err)) orderNumber += 1 continue } var orderId string if c.Bool("dry-run") { orderId = "DRY-RUN" } else { orderId = strings.Join(order.TransactionIds, ",") } log.WithFields(logrus.Fields{ "action": "btd", "order": order.Description.Order, "orderId": orderId, "order-number": orderNumber + 1, "dryrun": thisOrder["validate"], "orderType": thisOrder["orderType"], "volume": thisOrder["volume"], "askPrice": k.Ask, "price": thisOrder["price"], "orderFlags": thisOrder["oflags"], "userref": thisOrder["userref"], }).Info(fmt.Sprintf("Order Placed %d", orderNumber+1)) orderNumber += 1 } return "", nil } func (k *Kraken) Stack(c *cli.Context) (result string, e error) { k.UserRef = 100 log.WithFields(logrus.Fields{ "action": "stack", "userRef": k.UserRef, }).Info("Stacking some sats on " + k.Name) log.WithFields(logrus.Fields{ "action": "stack", "crypto": k.Crypto, "cryptoBalance": k.BalanceCrypto, "fiat": k.Fiat, "fiatBalance": k.BalanceFiat, "ask": k.Ask, }).Debug("Balance before placing the Order") volume := (c.Float64("amount") / k.AskFloat) orderArgs, err := k.createOrderArgs(c, volume, k.Ask, false) if err != nil { return "", fmt.Errorf("Failed to create args to place order: %s", err) } // Place the Order order, err := k.Api.AddOrder(k.Pair, "buy", orderArgs["orderType"], orderArgs["volume"], orderArgs) if err != nil { log.WithFields(logrus.Fields{ "action": "btd", "userref": k.UserRef, }).Error(fmt.Sprintf("Error Creating order: %s", err)) return "", fmt.Errorf("Failed to place order: %s", err) } var orderId string if c.Bool("dry-run") { orderId = "DRY-RUN" } else { orderId = strings.Join(order.TransactionIds, ",")
3e5067aa38cbcca3b42ceed5aa391634bfc3a212
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Handle cancel only mode from Kraken
sats-stacker/exchange/kraken.go
Line 200 in db77283
3e5067aa38cbcca3b42ceed5aa391634bfc3a212
The text was updated successfully, but these errors were encountered: