btcmarkets

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 15, 2018 License: MIT Imports: 12 Imported by: 0

README

Go BTC Markets

This package is a Go (Golang) effort to use the BTC Markets API, available at https://api.btcmarkets.net and documented on the BTCMarkets/API project. Please visit the API documentation to find out more about the service API. This is simply an API client.

This package is not affiliated with or created by BTC Markets Pty Ltd, but is intended to assist people in using their API with Go.

Getting Started

You will need Go installed to use this package.

Then run a go get to grab the latest source for this package.

go get -u github.com/dangrier/gobtcmarkets

You will also need a public and private key pair generated from your account at BTC Markets.

Installing / Usage

To use this package, include it in your imports.

import "github.com/dangrier/gobtcmarkets"

The package itself is exported on the name btcmarkets.

Example - Basic Dumb Bot
// Filename: basicbot.go
//
// Basic Bot finds out how many bitcoins it can buy with your available balance,
// then buys them, then waits to hit a profit threshold, sells the bitcoin, and
// moves the profit to your chosen account.
//
// Please be aware! If you run this - make sure you understand what it is doing first!
// There is also a chance that profit may never be realised, which is why this is a
// basic (dumb) bot.
//
// It may seem like some sections of this code will send a large amount of requests
// to the API, however the rate limiting will prevent this - all you have to worry
// about is making the API calls!
package main

import (
	"fmt"
	"log"
	"time"

	"github.com/dangrier/gobtcmarkets"
)

func main() {
	// Create a new btcmarkets.Client object
	cl, err := btcmarkets.NewClient("YOUR PUBLIC KEY HERE", "YOUR PRIVATE/SECRET KEY HERE")
	if err != nil {
		log.Fatal(err)
	}

	// Check your starting account balances
	bal, err := cl.AccountBalance()
	if err != nil {
		log.Fatal(err)
	}

	// Retrieve the AUD balance from list of balances
	//
	// The ToAmountDecimal() method converts between API number forms Whole and Decimal.
	// If the method is not used to convert, the number will be out by an order of 10^8!
	aud := bal.GetBalance(btcmarkets.CurrencyAUD).ToAmountDecimal()

	fmt.Printf("%d is $%.2f\n", bal.GetBalance(btcmarkets.CurrencyAUD), aud)

	// Get the current trading fee
	fee, err := cl.AccountTradingFee(btcmarkets.InstrumentBitcoin, btcmarkets.CurrencyAUD)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Fee is %.4fx\n", fee.TradingFee.ToAmountDecimal())

	// The usable amount of cash is the total amount minus the fee component
	var usable btcmarkets.AmountDecimal
	usable = (aud - (fee.TradingFee.ToAmountDecimal() * aud)).TrimCurrency()
	// Triming off the trailing decimal places after 2 places is required by the API

	fmt.Printf("Usable amount is $%.2f\n", usable)

	// Get the current market rate for AUD/BTC
	rate, err := cl.MarketTick(btcmarkets.InstrumentBitcoin, btcmarkets.CurrencyAUD)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Last $/BTC was $%.2f\n", rate.Last)

	// Determine how many coins to buy!
	coins := usable / rate.Last

	fmt.Printf("$%.2f is worth %.10fBTC\n", usable, coins)

	// Place an order for bitcoin
	order, err := cl.OrderCreate(btcmarkets.CurrencyAUD, btcmarkets.InstrumentBitcoin, rate.Last.ToAmountWhole(), coins.ToAmountWhole(), btcmarkets.Bid, btcmarkets.Market)
	if err != nil {
		log.Fatal(err)
	}

	// Wait for the order to be matched
	fmt.Printf("Order %d placed, awaiting fully matched state", order.ID)
	breakout := false
	for {
		m, _ := cl.OrderHistory(btcmarkets.CurrencyAUD, btcmarkets.InstrumentBitcoin, 10, 0)

		if !m.Success {
			log.Fatal("Something went wrong checking the order status! Please check manually!")
		}

		for _, o := range m.Orders {
			if o.OrderID == order.ID {
				if o.Status == btcmarkets.OrderStatusCancelled ||
					o.Status == btcmarkets.OrderStatusError ||
					o.Status == btcmarkets.OrderStatusFailed {
					log.Fatal(o.Error)
				}
				if o.Status == btcmarkets.OrderStatusFullyMatched {
					breakout = true
					fmt.Print("...MATCHED!\n\n")
					break
				} else {
					//fmt.Print(".")
					fmt.Printf("Status: %s\n", o.Status)
				}
			}
		}

		if breakout {
			break
		}
	}

	// YOU NOW HAVE BITCOIN!

	// Set the profit threshold for selling here
	profitThreshold := btcmarkets.AmountDecimal(0.01)
	fmt.Printf("Starting to check for profits with a threshold of %.2fx:\n", profitThreshold)
	var currentValue btcmarkets.MarketTickData
	for {
		// Check value of a bitcoin
		currentValue, err = cl.MarketTick(btcmarkets.InstrumentBitcoin, btcmarkets.CurrencyAUD)
		if err != nil {
			log.Fatal(err)
		}

		profit := (currentValue.Last * coins) - (currentValue.Last * coins * fee.TradingFee.ToAmountDecimal()) - usable

		if profit >= profitThreshold {
			break
		}

		fmt.Printf("%s - Profit: $%.2f (target: $%.2f)\n", time.Now().Format("2006-01-02 15:04:05"), profit, (profitThreshold * usable))
	}

	// YOU NOW HAVE ENOUGH PROFIT!

	// Sell the bitcoin
	sellOrder, err := cl.OrderCreate(btcmarkets.CurrencyAUD, btcmarkets.InstrumentBitcoin, rate.Last.ToAmountWhole(), coins.ToAmountWhole(), btcmarkets.Ask, btcmarkets.Market, "ABC123")
	if err != nil {
		log.Fatal(err)
	}

	// Wait for the order to be matched
	fmt.Printf("\n\nSell order %d placed, awaiting fully matched state", sellOrder.ID)
	breakout = false
	for {
		m, _ := cl.OrderHistory(btcmarkets.CurrencyAUD, btcmarkets.InstrumentBitcoin, 10, 0)

		if !m.Success {
			log.Fatal("Something went wrong checking the order status! Please check manually!")
		}

		for _, o := range m.Orders {
			if o.OrderID == sellOrder.ID {
				if o.Status == btcmarkets.OrderStatusCancelled ||
					o.Status == btcmarkets.OrderStatusError ||
					o.Status == btcmarkets.OrderStatusFailed {
					log.Fatal(o.Error)
				}
				if o.Status == btcmarkets.OrderStatusFullyMatched {
					breakout = true
					fmt.Print("...MATCHED!\n\n")
					break
				} else {
					fmt.Print(".")
				}
			}
		}

		if breakout {
			break
		}
	}

	fmt.Printf("DONE! Made $%.2f profit!", profitThreshold*usable)
}

Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

Versions prefixed with major version 0 (0.X.X) should be considered initial development versions and should not be relied upon for non-breaking changes.

Authors

If you like to show your support for the work that went into creating this library feel free to send Dan a beer at Bitcoin: 1CynPcMe1ZnHV3r2Zoi7snLMmj1RWSDsXy

License

This project is licensed under the MIT License - see the LICENSE file for details

Documentation

Index

Constants

View Source
const (
	// OrderStatusNew is an order which is created but has not yet been placed
	OrderStatusNew = "New"

	// OrderStatusPlaced is a placed order which is unfilled
	OrderStatusPlaced = "Placed"

	// OrderStatusFailed is an order which has failed
	OrderStatusFailed = "Failed"

	// OrderStatusError is an order which has failed due to an error
	OrderStatusError = "Error"

	// OrderStatusCancelled is an order cancelled by the client
	OrderStatusCancelled = "Cancelled"

	// OrderStatusPartiallyCancelled is an order that has been partially
	// completed, but cancelled before fully matched / completed
	OrderStatusPartiallyCancelled = "Partially Cancelled"

	// OrderStatusFullyMatched is a completed successful order
	OrderStatusFullyMatched = "Fully Matched"

	// OrderStatusPartiallyMatched is a partially completed order for which some
	// of the instrument has been traded, but not enough to complete the order
	OrderStatusPartiallyMatched = "Partially Matched"
)
View Source
const (
	// BaseURL is the protocol, and domain of the API to connect to.
	BaseURL = "https://api.btcmarkets.net"
)

Variables

This section is empty.

Functions

func NewRequest added in v0.2.0

func NewRequest(method, path string, data interface{}) (req *http.Request, bodyString string, err error)

NewRequest returns a new HTTP request.

Types

type AccountBalanceItem added in v0.2.0

type AccountBalanceItem struct {
	Currency Currency    `json:"currency"`
	Balance  AmountWhole `json:"balance"`
	Pending  AmountWhole `json:"pendingFunds"`
}

AccountBalanceItem is the data structure that represents currency account balance.

func (*AccountBalanceItem) String added in v0.2.0

func (ab *AccountBalanceItem) String() string

type AccountBalanceResponse added in v0.2.0

type AccountBalanceResponse []AccountBalanceItem

AccountBalanceResponse represents the JSON data structure sent to the GET /account/balance endpoint.

func (AccountBalanceResponse) GetBalance added in v0.2.0

func (a AccountBalanceResponse) GetBalance(currency Currency) AmountWhole

GetBalance returns the balance of the provided currency.

type AccountTradingFeeResponse added in v0.2.0

type AccountTradingFeeResponse struct {
	Success      bool        `json:"success"`
	ErrorCode    int         `json:"errorCode"`
	ErrorMessage string      `json:"errorMessage"`
	TradingFee   AmountWhole `json:"tradingFeeRate"`
	Volume30Days AmountWhole `json:"volume30Day"`
}

AccountTradingFeeResponse represents the JSON data structure sent to the GET /account/:instrument/:currency/tradingfee endpoint.

type AmountDecimal

type AmountDecimal float64

AmountDecimal is a float type which represents the API numbers returned which can have decimal places.

The AmountDecimal is 1/100000000 of an AmountWhole.

func (AmountDecimal) ToAmountWhole

func (amount AmountDecimal) ToAmountWhole() AmountWhole

ToAmountWhole converts from AmountDecimal to AmountWhole by multiplication by 100000000 (used by API)

func (AmountDecimal) TrimCurrency

func (amount AmountDecimal) TrimCurrency() AmountDecimal

type AmountWhole

type AmountWhole int64

AmountWhole is an integer type which represents the API numbers returned which can have decimal places.

The AmountWhole is 100000000x an AmountDecimal.

func (AmountWhole) ToAmountDecimal

func (amount AmountWhole) ToAmountDecimal() AmountDecimal

ToAmountDecimal converts from AmountWhole to AmountDecimal by division by 100000000 (used by API)

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the main struct type representing an interface with the API as a particular client user. It is stateless and obtains state/object information from API calls. The client is limited to 25 calls per 10 seconds on certain API endpoints, and 10 calls per 10 seconds on others. It is concurrency safe. (documented at https://github.com/BTCMarkets/API/wiki/faq)

func NewClient

func NewClient(key, secret string) (*Client, error)

NewClient constructs a new Client for communicating with the BTC Markets API. Both your API key and secret are required. The secret should be provided as displayed in your BTC Markets account, as a base-64 encoded string.

func (*Client) AccountBalance

func (c *Client) AccountBalance() (*AccountBalanceResponse, error)

AccountBalance implements the GET /account/balance endpoint.

func (*Client) AccountTradingFee

func (c *Client) AccountTradingFee(instrument Instrument, currency Currency) (*AccountTradingFeeResponse, error)

AccountTradingFee implements the /account/:instrument/:currency/tradingfee endpoint.

func (*Client) Get added in v0.2.0

func (c *Client) Get(path string, v interface{}, rateLimit RateLimitValue) error

Get handles a GET request to any BTC Markest API endpoint.

func (*Client) Limit10

func (c *Client) Limit10() error

Limit10 performs rate limiting of 10x / 10secs

func (*Client) MarketOrderbook

func (c *Client) MarketOrderbook(instrument Instrument, currency Currency) (*MarketOrderbookResponse, error)

MarketOrderbook implements the GET /market/:instrument/:currency/orderbook endpoint.

func (*Client) MarketTick

func (c *Client) MarketTick(instrument Instrument, currency Currency) (*MarketTickResponse, error)

MarketTick implements the GET /market/:instrument/:currency/tick endpoint.

func (*Client) MarketTrades

func (c *Client) MarketTrades(instrument Instrument, currency Currency, since OrderID) (*MarketTradesResponse, error)

MarketTrades implements the GET /market/:instrument/:currency/trades endpoint.

"since" is an optional parameter which, when greater than 0 will only get MarketTrades which occurred since the supplied trade ID.

func (*Client) OrderCancel

func (c *Client) OrderCancel(orderIDs ...OrderID) (*OrderCancelResponse, error)

OrderCancel implements the POST /order/cancel endpoint.

func (*Client) OrderCreate

func (c *Client) OrderCreate(
	currency Currency,
	instrument Instrument,
	price AmountWhole,
	volume AmountWhole,
	side OrderSide,
	ordertype OrderType,
	requestID string,
) (*OrderCreateResponse, error)

OrderCreate implements the POST /order/create endpoint.

func (*Client) OrderDetail

func (c *Client) OrderDetail(orderIDs ...OrderID) (*OrderDetailResponse, error)

OrderDetail implements the POST /order/detail API endpoint

func (*Client) OrderHistory

func (c *Client) OrderHistory(currency Currency, instrument Instrument, limit int, since OrderID) (*OrderHistoryResponse, error)

OrderHistory implements the POST /order/history API endpoint

func (*Client) OrderOpen

func (c *Client) OrderOpen(currency Currency, instrument Instrument, limit int, since OrderID) (*OrderOpenResponse, error)

OrderOpen implements the POST /order/open API endpoint

func (*Client) Post added in v0.2.0

func (c *Client) Post(path string, data interface{}, v interface{}, rateLimit RateLimitValue) error

Post handles a POST request to any BTC Markest API endpoint.

func (*Client) String

func (c *Client) String() string

String is present to implement the Stringer interface for the Client type.

func (*Client) WithdrawCrypto

func (c *Client) WithdrawCrypto(amount AmountWhole, currency Currency, address string) (*FundTransferWithdrawCryptoResponse, error)

WithdrawCrypto implements the POST /fundtransfer/withdrawCrypto API endpoint

func (*Client) WithdrawEFT

func (c *Client) WithdrawEFT(amount AmountWhole, currency Currency, accountName, accountNumber, bankName, bsb string) (*FundTransferWithdrawEFTResponse, error)

WithdrawEFT implements the POST /fundtransfer/withdrawEFT API endpoint

type Currency

type Currency string

Currency represents the name of a real-world or crypto currency

const (
	CurrencyAUD        Currency = "AUD"
	CurrencyBcash      Currency = "BCH"
	CurrencyBitcoin    Currency = "BTC"
	CurrencyEthereum   Currency = "ETH"
	CurrencyEthClassic Currency = "ETC"
	CurrencyLitecoin   Currency = "LTC"
	CurrencyRipple     Currency = "XRP"
)

Enumerated currencies.

type FundTransferWithdrawCryptoRequest added in v0.2.0

type FundTransferWithdrawCryptoRequest struct {
	Amount   AmountWhole `json:"amount"`
	Address  string      `json:"address"`
	Currency Currency    `json:"currency"`
}

FundTransferWithdrawCryptoRequest represents the required information for a crypto tranfer.

type FundTransferWithdrawCryptoResponse added in v0.2.0

type FundTransferWithdrawCryptoResponse struct {
	Success        bool        `json:"success"`
	ErrorCode      string      `json:"errorCode"`
	ErrorMessage   string      `json:"errorMessage"`
	Status         string      `json:"status"`
	FundTransferID int64       `json:"fundTransferId"`
	Description    string      `json:"description"`
	Created        int64       `json:"creationTime"`
	Currency       Currency    `json:"currency"`
	Amount         AmountWhole `json:"amount"`
	Fee            AmountWhole `json:"fee"`
}

FundTransferWithdrawCryptoResponse represents the JSON data structure returned from the POST /fundtransfer/withdrawCrypto endpoint.

type FundTransferWithdrawEFTRequest added in v0.2.0

type FundTransferWithdrawEFTRequest struct {
	AccountName   string      `json:"accountName"`
	AccountNumber string      `json:"accountNumber"`
	BankName      string      `json:"bankName"`
	BSB           string      `json:"bsbNumber"`
	Amount        AmountWhole `json:"amount"`
	Currency      Currency    `json:"currency"`
}

FundTransferWithdrawEFTRequest represents the required information for an external EFT tranfer

type FundTransferWithdrawEFTResponse added in v0.2.0

type FundTransferWithdrawEFTResponse struct {
	FundTransferWithdrawCryptoResponse
}

FundTransferWithdrawEFTResponse represents the JSON data structure returned from the POST /fundtransfer/withdrawEFT endpoint. it sharres the same data structure as FundTransferWithdrawCryptoResponse.

type Instrument

type Instrument string

Instrument represents the name of a crypto currency

const (
	InstrumentBcash      Instrument = "BCH"
	InstrumentBitcoin    Instrument = "BTC"
	InstrumentEthereum   Instrument = "ETH"
	InstrumentEthClassic Instrument = "ETC"
	InstrumentLitecoin   Instrument = "LTC"
	InstrumentRipple     Instrument = "XRP"
)

Enumerated instruments.

type MarketOrderbookResponse added in v0.2.0

type MarketOrderbookResponse struct {
	Bids       [][]float64 `json:"bids"`
	Asks       [][]float64 `json:"asks"`
	Currency   Currency    `json:"currency"`
	Instrument Instrument  `json:"instrument"`
	Timestamp  int64       `json:"timestamp"`
}

MarketOrderbookResponse represents the JSON data structure returned from the GET /market/:instrument/:currency/orderbook endpoint.

type MarketTickResponse added in v0.2.0

type MarketTickResponse struct {
	Bid        AmountDecimal `json:"bestBid"`
	Ask        AmountDecimal `json:"bestAsk"`
	Last       AmountDecimal `json:"lastPrice"`
	Currency   Currency      `json:"currency"`
	Instrument Instrument    `json:"instrument"`
	Timestamp  int64         `json:"timestamp"`
	Volume     float64       `json:"volume24h"`
}

MarketTickResponse represents the JSON data structure returned from the GET /market/:instrument/:currency/tick endpoint.

type MarketTradeDataItem added in v0.2.0

type MarketTradeDataItem struct {
	TradeID   TradeID       `json:"tid"`
	Amount    AmountDecimal `json:"amount"`
	Price     AmountDecimal `json:"price"`
	Timestamp int64         `json:"date"`
}

MarketTradeDataItem is the data structure that represents a single trade

func (*MarketTradeDataItem) String added in v0.2.0

func (mtdi *MarketTradeDataItem) String() string

String is a helper method for displaying a trade in human-readable format.

type MarketTradesResponse added in v0.2.0

type MarketTradesResponse []MarketTradeDataItem

MarketTradesResponse represents the JSON data structure returned from the GET /market/:instrument/:currency/trades endpoint.

func (*MarketTradesResponse) Describe added in v0.2.0

func (mtr *MarketTradesResponse) Describe() string

Describe is a helper method for displaying trades in human-readable format.

type OrderCancelData added in v0.2.0

type OrderCancelData struct {
	Success      bool    `json:"success"`
	ErrorCode    int     `json:"errorCode"`
	ErrorMessage string  `json:"errorMessage"`
	ID           OrderID `json:"id"`
}

OrderCancelData represents the JSON data structure of a cancel order.

type OrderCancelResponse added in v0.2.0

type OrderCancelResponse struct {
	Success      bool              `json:"success"`
	ErrorCode    int               `json:"errorCode"`
	ErrorMessage string            `json:"errorMessage"`
	Responses    []OrderCancelData `json:"responses"`
}

OrderCancelResponse represents the JSON data structure returned from the POST /order/cancel endpoint.

type OrderCreateRequest added in v0.2.0

type OrderCreateRequest struct {
	Currency        Currency    `json:"currency"`
	Instrument      Instrument  `json:"instrument"`
	Price           AmountWhole `json:"price"`
	Volume          AmountWhole `json:"volume"`
	OrderSide       OrderSide   `json:"orderSide"`
	OrderType       OrderType   `json:"ordertype"`
	ClientRequestID string      `json:"clientRequestId"`
}

OrderCreateRequest represents the JSON data structure sent to the POST /order/create endpoint.

type OrderCreateResponse added in v0.2.0

type OrderCreateResponse struct {
	Success         bool    `json:"success"`
	ErrorCode       int     `json:"errorCode"`
	ErrorMessage    string  `json:"errorMessage"`
	ID              OrderID `json:"id"`
	ClientRequestID string  `json:"clientRequestId"`
}

OrderCreateResponse represents the JSON data structure returned from the POST /order/create endpoint.

type OrderDataItem added in v0.2.0

type OrderDataItem struct {
	OrderID      OrderID              `json:"id"`
	Currency     Currency             `json:"currency"`
	Instrument   Instrument           `json:"instrument"`
	OrderSide    OrderSide            `json:"orderSide"`
	OrderType    OrderType            `json:"ordertype"`
	Created      int64                `json:"creationTime"`
	Status       OrderStatus          `json:"status"`
	ErrorMessage string               `json:"errorMessage"`
	Price        AmountWhole          `json:"price"`
	Volume       AmountWhole          `json:"volume"`
	VolumeOpen   AmountWhole          `json:"openVolume"`
	Trades       []OrderTradeDataItem `json:"trades"`
}

OrderDataItem is the data structure that represents a single order

type OrderDetailResponse added in v0.2.0

type OrderDetailResponse struct {
	Success      bool            `json:"success"`
	ErrorCode    string          `json:"errorCode"`
	ErrorMessage string          `json:"errorMessage"`
	Orders       []OrderDataItem `josn:"orders"`
}

OrderDetailResponse represents the JSON data structure returned from the POST /order/detail endpoint.

type OrderHistoryRequest added in v0.2.0

type OrderHistoryRequest struct {
	Currency   Currency   `json:"currency"`
	Instrument Instrument `json:"instrument"`
	Limit      int        `json:"limit"`
	Since      OrderID    `json:"since"`
}

OrderHistoryRequest represents the JSON data structure sent to the POST /order/history endpoint.

type OrderHistoryResponse added in v0.2.0

type OrderHistoryResponse struct {
	OrderDetailResponse
}

OrderHistoryResponse represents the JSON data structure returned from the POST /order/history endpoint. It shares the same data structure as OrderDetailResponse

type OrderID

type OrderID int64

OrderID is an integer representing the returned ID of a created order

type OrderOpenRequest added in v0.2.0

type OrderOpenRequest struct {
	OrderHistoryRequest
}

OrderOpenRequest represents the JSON data structure sent to the POST /order/open endpoint. It shares the same data structure as OrderHistoryRequest

type OrderOpenResponse added in v0.2.0

type OrderOpenResponse struct {
	OrderDetailResponse
}

OrderOpenResponse represents the JSON data structure returned from the POST /order/open endpoint. It shares the same data structure as OrderDetailResponse

type OrderSide

type OrderSide string

OrderSide is used when creating an order to state whether the order is an ask (sell), or a bid (buy).

const (
	Ask OrderSide = "Ask"
	Bid OrderSide = "Bid"
)

Enumerated order sides

type OrderStatus

type OrderStatus string

OrderStatus is a string which describes the status of the order after being made.

type OrderTradeDataItem added in v0.2.0

type OrderTradeDataItem struct {
	TradeID     TradeID     `json:"id"`
	Created     int64       `json:"creationTime"`
	Description string      `json:"description"`
	Price       AmountWhole `json:"price"`
	Volume      AmountWhole `json:"volume"`
	Fee         AmountWhole `json:"fee"`
}

OrderTradeDataItem is the data structure that represents a single trade

type OrderType

type OrderType string

OrderType is used when creating an order to state whether the order is limited by the provided parameters, or whether to place the order at the market value.

const (
	Limit  OrderType = "Limit"
	Market OrderType = "Market"
)

Enumerated order types

type OrdersSpecificRequest added in v0.2.0

type OrdersSpecificRequest struct {
	Orders []OrderID `json:"orderIds"`
}

OrdersSpecificRequest is the data structure for sending multiple orders ids for endpoints that support that feature.

type RateLimitValue added in v0.2.0

type RateLimitValue int

RateLimitValue represent a rate limiting value in the form of an int

type TradeID

type TradeID int64

TradeID is a unique trade id.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL