Giter Site home page Giter Site logo

rodrigo-brito / ninjabot Goto Github PK

View Code? Open in Web Editor NEW
1.4K 20.0 171.0 3.04 MB

A fast trading bot platform for cryptocurrency in Go (Binance)

Home Page: https://rodrigo-brito.github.io/ninjabot/

License: MIT License

Go 95.82% Makefile 0.05% HTML 0.94% JavaScript 3.19%
trading-bot cryptocurrency bot binance go trading hacktoberfest golang crypto trading-strategies

ninjabot's Introduction

Ninjabot

tests codecov Go Reference Discord Discord

A fast cryptocurrency trading bot framework implemented in Go. Ninjabot permits users to create and test custom strategies for sport and future markets.

Docs: https://rodrigo-brito.github.io/ninjabot/

DISCLAIMER
This software is for educational purposes only. Do not risk money which you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS

Installation

go get -u github.com/rodrigo-brito/ninjabot/...

Examples of Usage

Check examples directory:

  • Paper Wallet (Live Simulation)
  • Backtesting (Simulation with historical data)
  • Real Account (Binance)

CLI

To download historical data you can download ninjabot CLI from:

  • Pre-build binaries in release page
  • Or with go install github.com/rodrigo-brito/ninjabot/cmd/ninjabot@latest

Example of usage

# Download candles of BTCUSDT to btc.csv file (Last 30 days, timeframe 1D)
ninjabot download --pair BTCUSDT --timeframe 1d --days 30 --output ./btc.csv

Backtesting Example

  • Backtesting a custom strategy from examples directory:
go run examples/backtesting/main.go

Output:

INFO[2023-03-25 13:54] [SETUP] Using paper wallet                   
INFO[2023-03-25 13:54] [SETUP] Initial Portfolio = 10000.000000 USDT 
---------+--------+-----+------+--------+--------+-----+----------+-----------+
|  PAIR   | TRADES | WIN | LOSS | % WIN  | PAYOFF | SQN |  PROFIT  |  VOLUME   |
+---------+--------+-----+------+--------+--------+-----+----------+-----------+
| ETHUSDT |      9 |   6 |    3 | 66.7 % |  3.407 | 1.3 | 21748.41 | 407769.64 |
| BTCUSDT |     14 |   6 |    8 | 42.9 % |  5.929 | 1.5 | 13511.66 | 448030.05 |
+---------+--------+-----+------+--------+--------+-----+----------+-----------+
|   TOTAL |     23 |  12 |   11 | 52.2 % |  4.942 | 1.4 | 35260.07 | 855799.68 |
+---------+--------+-----+------+--------+--------+-----+----------+-----------+

-- FINAL WALLET --
0.0000 BTC = 0.0000 USDT
0.0000 ETH = 0.0000 USDT
45260.0735 USDT

----- RETURNS -----
START PORTFOLIO     = 10000.00 USDT
FINAL PORTFOLIO     = 45260.07 USDT
GROSS PROFIT        =  35260.073493 USDT (352.60%)
MARKET CHANGE (B&H) =  407.09%

------ RISK -------
MAX DRAWDOWN = -11.76 %

------ VOLUME -----
BTCUSDT         = 448030.05 USDT
ETHUSDT         = 407769.64 USDT
TOTAL           = 855799.68 USDT
-------------------
Chart available at http://localhost:8080

Plot result

Features

Binance Spot Binance Futures
Order Market ๐Ÿ†— ๐Ÿ†—
Order Market Quote ๐Ÿ†—
Order Limit ๐Ÿ†— ๐Ÿ†—
Order Stop ๐Ÿ†— ๐Ÿ†—
Order OCO ๐Ÿ†—
Backtesting ๐Ÿ†— ๐Ÿ†—
  • Backtesting

    • Paper Wallet (Live Trading with fake wallet)
    • Load Feed from CSV
    • Order Limit, Market, Stop Limit, OCO
  • Bot Utilities

    • CLI to download historical data
    • Plot (Candles + Sell / Buy orders, Indicators)
    • Telegram Controller (Status, Buy, Sell, and Notification)
    • Heikin Ashi candle type support
    • Trailing stop tool
    • In app order scheduler

Roadmap

  • Include Web UI Controller
  • Include more chart indicators - Details

Exchanges

Currently, we only support Binance exchange. If you want to include support for other exchanges, you need to implement a new struct that implements the interface Exchange. You can check some examples in exchange directory.

Support the project

Address
BTC bc1qpk6yqju6rkz33ntzj8kuepmynmztzydmec2zm4
ETH 0x2226FFe4aBD2Afa84bf7222C2b17BBC65F64555A
LTC ltc1qj2n9r4yfsm5dnsmmtzhgj8qcj8fjpcvgkd9v3j

Patreon: https://www.patreon.com/ninjabot_github

ninjabot's People

Contributors

andreimerfu avatar avoidaway avatar darrenli6 avatar daydoing avatar dependabot[bot] avatar earvinkayonga avatar gunebakan avatar jietea avatar leoviggiano avatar lucaszatta avatar panapol-p avatar rafaelrubbioli avatar ramilexe avatar rene-post avatar rizalgowandy avatar robertkwiatkowski avatar rodrigo-brito avatar ronaldpetty avatar shatakshi0805 avatar spreeker avatar sudiptog81 avatar testwill avatar theballmarcus avatar victorassunc avatar vinicio avatar yaki3355 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ninjabot's Issues

Include new chart indicators

We can create and initialize indicators for chats in Ninjabot.

Example:

chart := plot.NewChart(plot.WithIndicators(
indicator.EMA(8, "red"),
indicator.EMA(21, "#000"),
indicator.RSI(14, "purple"),
indicator.Stoch(8, 3, "red", "blue"),
))

A indicator is a simple struct that implements the follow interface:

type Indicator interface {
	Name() string
	Overlay() bool
	Metrics() []IndicatorMetric
	Load(dataframe *model.Dataframe)
}

Here, we have a simple example of Exponential Moving Average (EMA): https://github.com/rodrigo-brito/ninjabot/blob/516e75064107daf73495bf1d091b5dc1cba18c09/plot/indicator/ema.go

Indicators Roadmap

Extract chart from ninjabot core

We have some issues with the evolution of the Javascript Module. We can use only vanilla JS because it's is compiled in runtime with ES Build.

A good approach will be an isolated development of Chart Module. Then, we can use complex components with modern libraries such as React / Vue.

The chart in Ninjabot core will work exactly as it does now. But, it will include only a simple HTML template with an external import.

backtesting sometimes crashes with "concurrent map write"

Roughly 1/10 runs I get a crash, fairly repeatable.

% go run example/backtesting/main.go
INFO[2021-05-22 13:39] [SETUP] Using paper wallet
INFO[2021-05-22 13:39] [SETUP] Initial Portfolio = 10000.000000 USDT
fatal error: concurrent map writes

goroutine 55 [running]:
runtime.throw(0x45f51b3, 0x15)
	/usr/local/go/src/runtime/panic.go:1117 +0x72 fp=0xc000267ce0 sp=0xc000267cb0 pc=0x4039a12
runtime.mapassign_faststr(0x456fce0, 0xc0000a5710, 0x45eb910, 0x7, 0x0)
	/usr/local/go/src/runtime/map_faststr.go:291 +0x3d8 fp=0xc000267d48 sp=0xc000267ce0 pc=0x40181d8
github.com/rodrigo-brito/ninjabot/pkg/plot.(*Chart).OnCandle(0xc00000e0d8, 0x45eb910, 0x7, 0x0, 0xed7f2d740, 0x0, 0x409a2c1eb851eb85, 0x409a0df5c28f5c29, 0x409a02851eb851ec, 0x409a340000000000, ...)
	/Users/myusername/Documents/Projects/ninjabot/pkg/plot/chart.go:29 +0x67 fp=0xc000267da0 sp=0xc000267d48 pc=0x430ecc7
github.com/rodrigo-brito/ninjabot.CandleSubscriber.OnCandle-fm(0x45eb910, 0x7, 0x0, 0xed7f2d740, 0x0, 0x409a2c1eb851eb85, 0x409a0df5c28f5c29, 0x409a02851eb851ec, 0x409a340000000000, 0x40ccf7999999999a, ...)
	/Users/myusername/Documents/Projects/ninjabot/ninjabot.go:37 +0x55 fp=0xc000267e18 sp=0xc000267da0 pc=0x43f3bd5
github.com/rodrigo-brito/ninjabot/pkg/exchange.(*DataFeedSubscription).Start.func1(0xc00002a740, 0xc0000363c0, 0xc00002a560, 0xb, 0xc0000207b0)
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/exchange.go:127 +0x278 fp=0xc000267fb8 sp=0xc000267e18 pc=0x43cf998
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:1371 +0x1 fp=0xc000267fc0 sp=0xc000267fb8 pc=0x406f301
created by github.com/rodrigo-brito/ninjabot/pkg/exchange.(*DataFeedSubscription).Start
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/exchange.go:115 +0x139

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc00002a748)
	/usr/local/go/src/runtime/sema.go:56 +0x45
sync.(*WaitGroup).Wait(0xc00002a740)
	/usr/local/go/src/sync/waitgroup.go:130 +0x65
github.com/rodrigo-brito/ninjabot/pkg/exchange.(*DataFeedSubscription).Start(0xc0000363c0)
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/exchange.go:137 +0x1a5
github.com/rodrigo-brito/ninjabot.(*NinjaBot).Run(0xc0001f50a0, 0x467d770, 0xc0000aa000, 0x2, 0x2)
	/Users/myusername/Documents/Projects/ninjabot/ninjabot.go:185 +0x38f
main.main()
	/Users/myusername/Documents/Projects/ninjabot/example/backtesting/main.go:75 +0x5b2

goroutine 9 [select]:
database/sql.(*DB).connectionOpener(0xc0000a61a0, 0x467d738, 0xc000036040)
	/usr/local/go/src/database/sql/sql.go:1133 +0xb5
created by database/sql.OpenDB
	/usr/local/go/src/database/sql/sql.go:740 +0x12a

goroutine 13 [chan receive]:
github.com/rodrigo-brito/ninjabot/pkg/order.(*Feed).Start.func1(0xc000020480, 0x45eb520, 0x7, 0xc000020540)
	/Users/myusername/Documents/Projects/ninjabot/pkg/order/feed.go:54 +0x93
created by github.com/rodrigo-brito/ninjabot/pkg/order.(*Feed).Start
	/Users/myusername/Documents/Projects/ninjabot/pkg/order/feed.go:53 +0xfe

goroutine 14 [chan receive]:
github.com/rodrigo-brito/ninjabot/pkg/order.(*Feed).Start.func1(0xc000020480, 0x45eb910, 0x7, 0xc000020560)
	/Users/myusername/Documents/Projects/ninjabot/pkg/order/feed.go:54 +0x93
created by github.com/rodrigo-brito/ninjabot/pkg/order.(*Feed).Start
	/Users/myusername/Documents/Projects/ninjabot/pkg/order/feed.go:53 +0xfe

goroutine 15 [chan receive]:
github.com/rodrigo-brito/ninjabot/pkg/order.(*Controller).Start.func1(0xc00007a190)
	/Users/myusername/Documents/Projects/ninjabot/pkg/order/controller.go:142 +0x8d6
created by github.com/rodrigo-brito/ninjabot/pkg/order.(*Controller).Start
	/Users/myusername/Documents/Projects/ninjabot/pkg/order/controller.go:141 +0x3f

goroutine 16 [chan send]:
github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription.func1(0xc0000a5320, 0xc0000a5350, 0xc00002a6e0, 0xb, 0xc0000962a0)
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:205 +0xf9
created by github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:203 +0x15f

goroutine 50 [chan send]:
github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription.func1(0xc0000a5320, 0xc0000a5350, 0xc00002a6f0, 0xb, 0xc000096360)
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:205 +0xf9
created by github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:203 +0x15f

goroutine 51 [chan send]:
github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription.func1(0xc0000a5320, 0xc0000a5350, 0xc00002a700, 0xb, 0xc000096420)
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:205 +0xf9
created by github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:203 +0x15f

goroutine 52 [chan send]:
github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription.func1(0xc0000a5320, 0xc0000a5350, 0xc00002a710, 0xb, 0xc0000964e0)
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:205 +0xf9
created by github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:203 +0x15f

goroutine 53 [chan send]:
github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription.func1(0xc0000a5320, 0xc0000a5350, 0xc00002a720, 0xb, 0xc0000965a0)
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:205 +0xf9
created by github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:203 +0x15f

goroutine 54 [chan send]:
github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription.func1(0xc0000a5320, 0xc0000a5350, 0xc00002a730, 0xb, 0xc000096660)
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:205 +0xf9
created by github.com/rodrigo-brito/ninjabot/pkg/exchange.CSVFeed.CandlesSubscription
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/csvfeed.go:203 +0x15f

goroutine 56 [runnable]:
github.com/markcheno/go-talib.ema(0xc00021e000, 0xcde, 0x1000, 0x15, 0x3fb745d1745d1746, 0xc000911500, 0xcde, 0xcde)
	/Users/myusername/Documents/Projects/go/pkg/mod/github.com/markcheno/[email protected]/talib.go:127 +0xdd
github.com/markcheno/go-talib.Ema(...)
	/Users/myusername/Documents/Projects/go/pkg/mod/github.com/markcheno/[email protected]/talib.go:141
github.com/rodrigo-brito/ninjabot/example.MyStrategy.Indicators(0xc00007e900)
	/Users/myusername/Documents/Projects/ninjabot/example/strategy.go:25 +0x125
github.com/rodrigo-brito/ninjabot/pkg/strategy.(*Controller).OnCandle(0xc0000a5890, 0x45eb520, 0x7, 0x0, 0xed7fa1780, 0x0, 0x40ecf4b99999999a, 0x40ecd294cccccccd, 0x40ecafdeb851eb85, 0x40ed31f70a3d70a4, ...)
	/Users/myusername/Documents/Projects/ninjabot/pkg/strategy/controller.go:54 +0x243
github.com/rodrigo-brito/ninjabot/pkg/exchange.(*DataFeedSubscription).Start.func1(0xc00002a740, 0xc0000363c0, 0xc00002a534, 0xb, 0xc000020780)
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/exchange.go:127 +0x278
created by github.com/rodrigo-brito/ninjabot/pkg/exchange.(*DataFeedSubscription).Start
	/Users/myusername/Documents/Projects/ninjabot/pkg/exchange/exchange.go:115 +0x139
exit status 2

I am on go version go1.16.3 darwin/amd64
commit 985b1c0

csv float precision default %f is not good enough for many assets. BIG FAT BUG!!

func (c Candle) ToSlice() []string

uses %f to describe floats. close, open high and low prices.

which in the case of shiba inu and others gives candles like these missing some data...

1632200100**,0.000007,0.000007,0.000007,0.000007,**6735653639.0,62
1632200400,0.000007,0.000007,0.000007,0.000007,6397726216.0,115
1632200700,0.000007,0.000007,0.000007,0.000007,14491282312.0,71

It ofcourse should contain: 0.00000798374 a lot more precision.
either use %.10f or default precision leading to large csv files of use symbol info provided binance.

My backtester had unreasonable good results for shiba. Now I know why....

Implement Bollinger Bands in Ninjabot Chart

To implement Bolinger Bands, we need to implement a new Struct similar to EMA: https://github.com/rodrigo-brito/ninjabot/blob/516e75064107daf73495bf1d091b5dc1cba18c09/plot/indicator/ema.go

The struct must implement the following interface:

type Indicator interface {
	Name() string
	Overlay() bool
	Metrics() []IndicatorMetric
	Load(dataframe *model.Dataframe)
}

BB Indicator contains 3 metrics (upper band, mid-band, and lower band)
You can use TALIB to get these values: talib.BBands(...)

To test the new indicator, you can include it here:

chart := plot.NewChart(plot.WithIndicators(
indicator.EMA(8, "red"),
indicator.EMA(21, "#000"),
indicator.RSI(14, "purple"),
indicator.Stoch(8, 3, "red", "blue"),
))

And run: go run examples/backtesting/main.go

Include progressbar in dowloading

Description

sometime when I downlaod big data I didnt know it is working or not (no result more than 10 min)

Relevant Context

add progress bar to show progress by revise func Download in download.go

Steps to Solution(s)

revise download.go and try to download big historical data

Links to Resources

progress bar https://github.com/schollz/progressbar

Replace global variable in plot module with a REST API

In plot module, we load candle information with a template. The vales are loaded in a global variable and read buy the JS script.
Template load: https://github.com/rodrigo-brito/ninjabot/blob/main/plot/chart.go#L136-L138
Global variable: https://github.com/rodrigo-brito/ninjabot/blob/main/plot/assets/chart.html#L14

Maybe, we can remove this, and create a simple API to transmit the data with a REST endpoint. For example:
Request: http:localhost:8080/candles/btcusdt
Response: {"candles": [ ... ]}

  • Create a REST API to serve candle data
  • Replace global variable with a http request

Improve Telegram error handling

When the bot fail placing an order, the final message do not includes information about the order. For example:
image

In this case, we can include information about the trading pair, order size, and order type.

Merge indictors from strategy and chart module

Today, we need to define custom indicators in the Indicator function and also include custom indicators for the chart module.
When we need to change a parameter, we have to change in both places. A good improvement is a merge between both parts.

  • When we include an indicator in your strategy, it is automatically include in the chart module.
  • We have to provide an option to hide a given indicator
  • We have to provide an option to include additional indicators besides the ones from strategy

Create a template for issues and pull requests

Backtesting with OCO Order and multiple coins

We have an issue when backtesting strategies with OCO Orders and multiple coins. The final result is correct, but the table with results for each coin presents a wrong report.

  • Example with strategies.OCOSell and backtesting BTCUSDT and ETHUSDT.
+---------+--------+-----+------+--------+--------+------------+
|  PAIR   | TRADES | WIN | LOSS | % WIN  | PAYOFF |   PROFIT   |
+---------+--------+-----+------+--------+--------+------------+
| ETHUSDT |     23 |  21 |    2 | 91.3 % |  3.087 | 17942.7567 |
| BTCUSDT |     24 |  16 |    8 | 66.7 % |  1.937 |  4389.9813 |
+---------+--------+-----+------+--------+--------+------------+
|   TOTAL |     47 |  37 |   10 | 78.7 % |  2.500 | 22332.7380 |
+---------+--------+-----+------+--------+--------+------------+
--------------
WALLET SUMMARY
--------------
0.000000 ETH
0.000000 BTC
11000.000000 USDT
--------------
START PORTFOLIO =  10000 USDT
FINAL PORTFOLIO =  11000 USDT
GROSS PROFIT    =  1000.000000 USDT (10.00%)
MARKET CHANGE   =  396.71%

It's probably related to async execution of LIMIT / STOP orders.
The paperwallet validates the creation orders and assets limit, but in the end, the report does not consider the order of execution.

could the project support the strategy ?

////////////////////////////////////////////////////////////
// Copyright by HPotter v1.0 20/06/2014
// The Moving Average Crossover trading strategy is possibly the most popular
// trading strategy in the world of trading. First of them were written in the
// middle of XX century, when commodities trading strategies became popular.
// This strategy is a good example of so-called traditional strategies.
// Traditional strategies are always long or short. That means they are never
// out of the market. The concept of having a strategy that is always long or
// short may be scary, particularly in todayโ€™s market where you donโ€™t know what
// is going to happen as far as risk on any one market. But a lot of traders
// believe that the concept is still valid, especially for those of traders who
// do their own research or their own discretionary trading.
// This version uses crossover of moving average and its exponential moving average.
////////////////////////////////////////////////////////////
study(title="EMA & MA Crossover", shorttitle="EMA & MA Crossover", overlay = true)
LengthMA = input(10, minval=1)
LengthEMA = input(10,minval=1)
xMA = sma(close, LengthMA)
xEMA = ema(xMA, LengthEMA)
pos = iff(xEMA < xMA , 1,
iff(xEMA > xMA, -1, nz(pos[1], 0)))
barcolor(pos == -1 ? red: pos == 1 ? green : blue)
plot(xMA, color=red, title="MA")
plot(xEMA, color=blue, title="EMA")

Include SQN metric in the backtesting result

Van's System Quality Number (SQN) is a metric designed to assist traders in determining the strengths, desirability, quality etc of a trading system.

The following values suggest the following โ€œqualitiesโ€ :

  • 1.6 - 1.9 Below average
  • 2.0 - 2.4 Average
  • 2.5 - 2.9 Good
  • 3.0 - 5.0 Excellent
  • 5.1 - 6.9 Superb
  • 7.0 - Holy Grail

Formula: SquareRoot(NumberTrades) * Average(TradesProfit) / StdDev(TradesProfit)

More information: https://tradingtact.com/system-quality-number/

small websocket reliabilty improvements.

setting this to true helps.

binance.WebsocketKeepalive = true

func StartListenIn(ccandle chan Candle, cerr chan error, symbol Symbol, period string) error {

done, _, err := binance.WsKlineServe(string(symbol), period, func(event *binance.WsKlineEvent) {
	ccandle <- CandleFromWsKline(symbol, event.Kline)
}, func(err error) {
	cerr <- err
})

if err != nil {
	<-done
	return err
}
return nil

}

Im using this now:

func StartListenIn(ccandle chan Candle, cerr chan error, symbol Symbol, period string) error {

	done, _, err := binance.WsKlineServe(string(symbol), period, func(event *binance.WsKlineEvent) {
		ccandle <- CandleFromWsKline(symbol, event.Kline)
	}, func(err error) {
		cerr <- err
	})

	if err != nil {
		<-done
		return err
	}
	return nil
}


func (b *Binance) CandlesSubscription(symbol Symbol, period string) (chan Candle, chan error) {
	ccandle := make(chan Candle)
	cerr := make(chan error)

	binance.WebsocketKeepalive = true

	go func() {
		errors := 0
		for {
			err := StartListenIn(ccandle, cerr, symbol, period)

			if err != nil {
				cerr <- err
				log.Error(err)
				errors++
			}

			//lets continue..

			if errors > 10 {
				break
				panic(err)
			}
		}

		close(cerr)
		close(ccandle)
		return
	}()

	return ccandle, cerr
}

Update orders status with user data stream

In the discussion #130, @davidxiao suggest to use Binance user stream instead of orders polling.

When we have orders with status PENDNG, the bot will poll from Binance the order status each second. It can be replaced by a stream that receive updates from Binance.

We found the logic here:

ticker := time.NewTicker(c.tickerInterval)
for {
select {
case <-ticker.C:
c.updateOrders()
case <-c.finish:
ticker.Stop()
return
}
}

Fee Validation on Market orders

Trying to understand why taker/maker commission is added to validation for amounts when doing a market sell or buy on Binance

Lets say I have 10 ETH as balance, if I try to sell 10 ETH with bot currently, validate will fail ErrInsufficientFunds since it adds TakerCommission

How should I calculate the size to pass to a sell amount? Should I calculate it as 10 / (1 + b.userInfo.TakerCommission)?

Create MACD indicator for Ninjabot Chart

To implement MACD, we need to implement a new Struct similar to EMA: https://github.com/rodrigo-brito/ninjabot/blob/516e75064107daf73495bf1d091b5dc1cba18c09/plot/indicator/ema.go

The struct must implement the following interface:

type Indicator interface {
	Name() string
	Overlay() bool
	Metrics() []IndicatorMetric
	Load(dataframe *model.Dataframe)
}

MACD Indicator contains 3 metrics (value, signal, and histogram)
You can use TALIB to get these values: talib.Macd(...)

To test the new indicator, you can include it here:

chart := plot.NewChart(plot.WithIndicators(
indicator.EMA(8, "red"),
indicator.EMA(21, "#000"),
indicator.RSI(14, "purple"),
indicator.Stoch(8, 3, "red", "blue"),
))

And run: go run examples/backtesting/main.go

Include support for FTX

To support FTX Exchange, we need to implement a new struct with the follow interface.

type Exchange interface {
	Broker
	Feeder
}

type Feeder interface {
	AssetsInfo(pair string) model.AssetInfo
	LastQuote(ctx context.Context, pair string) (float64, error)
	CandlesByPeriod(ctx context.Context, pair, period string, start, end time.Time) ([]model.Candle, error)
	CandlesByLimit(ctx context.Context, pair, period string, limit int) ([]model.Candle, error)
	CandlesSubscription(ctx context.Context, pair, timeframe string) (chan model.Candle, chan error)
}

type Broker interface {
	Account() (model.Account, error)
	Position(pair string) (asset, quote float64, err error)
	Order(pair string, id int64) (model.Order, error)
	CreateOrderOCO(side model.SideType, pair string, size, price, stop, stopLimit float64) ([]model.Order, error)
	CreateOrderLimit(side model.SideType, pair string, size float64, limit float64) (model.Order, error)
	CreateOrderMarket(side model.SideType, pair string, size float64) (model.Order, error)
	CreateOrderMarketQuote(side model.SideType, pair string, quote float64) (model.Order, error)
	Cancel(model.Order) error
}
  • The Feeder will cover the data feed (candles)
  • The Broker will cover exchange operations.

We have some libraries to test: https://github.com/go-numb/go-ftx

Retrive pair list dinamicly from Binance

We have a static list of pairs available to trade in

var symbolAssetQuoteMap = map[string]AssetQuote{

But new coins are included and removed every day.

As an alternative, we can retrieve this data from Binance. This info is public and do not need a API key to request. As example, we collected this data here:

results, err := exchange.client.NewExchangeInfoService().Do(ctx)
if err != nil {
return nil, err
}
// Initialize with orders precision and assets limits
exchange.assetsInfo = make(map[string]AssetInfo)
for _, info := range results.Symbols {
tradeLimits := AssetInfo{
BaseAsset: info.BaseAsset,
QuoteAsset: info.QuoteAsset,
}

No data processed whth $SHIB

Hi, first of all, thank you for this project. I'm a professional c# architect and this project helps me a lot to learn about trading bots and go.

I tried the backtesting with the SHIB coin (I know.. just see how many loss the bot would have taken) but for some reason the output shows NaNs and zeros, the chart is empty.

$ go run example/backtesting/main.go
INFO[2021-05-25 07:22] [SETUP] Using paper wallet                   
INFO[2021-05-25 07:22] [SETUP] Initial Portfolio = 6000.000000 USDT 
+-------+--------+-----+------+-------+--------+--------+
| PAIR  | TRADES | WIN | LOSS | % WIN | PAYOFF | PROFIT |
+-------+--------+-----+------+-------+--------+--------+
+-------+--------+-----+------+-------+--------+--------+
| TOTAL |      0 |   0 |    0 | NAN % |    NAN | 0.0000 |
+-------+--------+-----+------+-------+--------+--------+
--------------
WALLET SUMMARY
--------------
6000.000000 USDT
--------------
START PORTFOLIO =  6000 USDT
FINAL PORTFOLIO =  6000 USDT
GROSS PROFIT    =  0.000000 USDT (0.00%)
MARKET CHANGE   =  NaN%

What could be the problem? SHIB developed itself as starting very high but falling since the first pump/dump.
Maybe the bot didn't tried a single trade and therefore the report is empty?

Thank you!

Bot finish afeter websocket close

When the websocket connection closes, the bot finish with the following error:

ERRO[2021-06-13 00:20] dataFeedSubscription/start: websocket: close 1001 (going away) 
ERRO[2021-06-13 00:22] dataFeedSubscription/start: websocket: close 1001 (going away) 
ERRO[2021-06-13 00:24] dataFeedSubscription/start: websocket: close 1001 (going away) 
ERRO[2021-06-13 01:00] dataFeedSubscription/start: websocket: close 1001 (going away) 

Maybe a connection retry with backoff works fine for this case.

the queue seems deadlock with latest changes,

With latest code change, the priority queue seems deadlock when tested by running paperwallet example.

wrapping the below code block in anonymous function works, but not so sure it has other potential issues.
https://github.com/rodrigo-brito/ninjabot/blob/main/model/priorityqueue.go#L30-L35

Reckon it's not necessary to use priority queue at all for the bot? sorry maybe i could be wrong because i have limited knowledge yet. but what I can think of is,

  • the bot doesn't maintain items in the queue, even potentially it's possible to accumulate some, but it's because the downstreams can not handle in time.

  • with above concern, then in most cases, it will be not used as expected because no items in it, or even if there are occasionally, it's no point to maintain the order according to time?

  • probably strategy controller is the place to maintain the queue if needed, but i think candles are maintained from onCandle as below, and it reckon be reliable?

https://github.com/rodrigo-brito/ninjabot/blob/main/strategy/controller.go#L34

It'll be great if you can pls share more thoughts :)

Thanks very much

Rename all references of `symbol` to `pair`

We have two references to the asset code. In some places, we named it as pair and in other places are symbol.

We need to patronize this and change all references to pair

For example:

func (d Downloader) Download(ctx context.Context, symbol, timeframe string, output string, options ...Option) error {

Include indicators on chart

Currently, we only display the price and orders in the ninjabot chart.

Maybe, we can include some options when creating the chart instance. eg:

chart := plot.NewChart(
    WithIndicator(section, indicator, options...)
)

Where section is a name of a group of indicators. eg: MACD
indicator is a function that receive a Dataframe and returns a Series
options are optional attributes, such as color, width, etc.

Futures support

Do you have any plan to add support for Binance futures in the near future? Thanks!

some questions or new features

Here are some questions or new features

  • how to implement trailing stop tool? it'd be great if can share more info?

  • what's thoughts for polling orders from exchange every second? how about using user data stream?

  • in strategy, currently Timeframe is a single frame, how about i will use both 15m and 1h for one asset for example? and if so, maybe need to resample the candles?

  • how about adding hooks in strategy, for scenarios: when putting order, i'd like to refer to outside sources, etc.

  • any thoughts about integrating with 3rd frontend tool? like tradingview, or open source tools, etc?

Wrong param parsing for CLI

I tried to use the ninjabot CLI (both Linux x64_86 version and Windows version) to download historical data. Same thing happens on v.0.0.1 and 0.0.2

If I use it as per the documentation
./ninjabot download --pair ETHUSDT --timeframe 1d --days 30 --output ./eth.csv

I get the following resutl:

NAME:
   download - download historical data

USAGE:
   ninjabot [global options] command [command options] [arguments...]

COMMANDS:
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --pair value, -p value       eg. BTCUSDT
   --days value, -d value       eg. 100 (default 30 days) (default: 0)
   --start value, -s value      eg. 2021-12-01 (default: (*time.Time)(nil))
   --end value, -e value        eg. 2020-12-31 (default: (*time.Time)(nil))
   --timeframe value, -t value  eg. 1h
   --output value, -o value     eg. ./btc.csv
   --help, -h                   show help (default: false)
2021/05/21 11:05:45 Required flags "pair, timeframe, output" not set

However passing the params before the download command - seems to work

 ./ninjabot --pair ETHUSDT --timeframe 1d --days 30 --output ./eth.csv download
INFO[0000] [SETUP] Using Binance exchange
INFO[0000] Downloading 30 candles of 1d for ETHUSDT
INFO[0000] Done!

Seems like either the documentation or the params parsing is wrong - let me know what the correct behaviour is and I can take a look and raise a PR.

Telegram `/stop` is not working

Telegram's command /stop is not working. We define the logic here:

func (c *Controller) Stop() {
if c.status == StatusRunning {
c.status = StatusStopped
c.updateOrders()
c.finish <- true
log.Info("Bot stopped.")
}
}

The idea is:

  • The bot will only execute orders when it is in running status. But it is not working.

Include button to export CSV with orders in chart page

We can include a new API endpoint, eg. https://localhost:8080/trades.csv which returns all trades performed in a given backtesting.
Then, we can include a button in the chart page to access this endpoint and download all trades performed.

Balanced portfolio settings

Hi all,

I'm writing a ninjabot client and I want to handle a balanced portfolio as in this project [1]. I saw that we can pass a pairs list to the ninjabot settings, but we cannot specify the weight for each asset. Are you interested on implementing this feature? if so, I could help with the implementation, I have good experience in Go. If not, I'll have to work in a separate fork to make use of this feature.

Thanks,
Jorge

[1] https://github.com/kdmukai/binance_bbb#customize-your-portfolio

Remove usage of Ent framework

We use Ent framework in a few operations when we need to register a new order or calculate the average profit. But we can replace with direct queries in SQLite.

Database schema: https://github.com/rodrigo-brito/ninjabot/blob/main/storage/schema/order.go

Libray usages

Improve `/balance` command performance

When we request the balance summary, ninjabot will request the position for each trading pair and report a summary.
But in this case, we request the same function multiple times: binance.Account()

func (b *Binance) Position(pair string) (asset, quote float64, err error) {
assetTick, quoteTick := SplitAssetQuote(pair)
acc, err := b.Account()
if err != nil {
return 0, 0, err
}
assetBalance := acc.Balance(assetTick)
quoteBalance := acc.Balance(quoteTick)
return assetBalance.Free + assetBalance.Lock, quoteBalance.Free + quoteBalance.Lock, nil
}

A good approach to improve the performance are: Request binance.Account() once and calculate the summary for all pairs in the same time.

code=-2014, API-key format invalid

Hello,

I am getting this error

ERRO[2022-01-23 05:00] <APIError> code=-2014, msg=API-key format invalid. 
ERRO[2022-01-23 05:00] <APIError> code=-2014, msg=API-key format invalid. 
ERRO[2022-01-23 09:00] <APIError> code=-2014, msg=API-key format invalid. 
ERRO[2022-01-23 09:00] <APIError> code=-2014, msg=API-key format invalid. 
ERRO[2022-01-23 13:00] <APIError> code=-2014, msg=API-key format invalid. 
ERRO[2022-01-23 13:00] <APIError> code=-2014, msg=API-key format invalid.

I am assuming it is coming from somewhere in here? https://github.com/rodrigo-brito/ninjabot/blob/main/exchange/binance.go

Any pointers as to why or how to solve this problem are welcome.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.