Skip to content
This repository has been archived by the owner on Feb 15, 2022. It is now read-only.

Commit

Permalink
Completely rework --buy_max_amt (#1494)
Browse files Browse the repository at this point in the history
* Completely rework --buy_max_amt

As much as i like the --buy_max_amt feature i was never a fan of how it was implemented, leave alone the naming being confusing at best as it simply doesn't just account for the amount the bot can buy but how much he is actually able to trade.

* Renames --buy_max_amt to --deposit for sake of clarification
* Using --buy_max_amt will still work but print a deprecated warning
* Instead of using --deposit to size buy orders use it to limit the balance available to the bot
* This allows us to use it together with --buy_pct
* --deposit will still account for asset held to calculate the available currency balance
* So if the asset held is covering --deposit the bot will have 0 currency (0% of currency balance) available, if not currency will be filled to the point it compensates for the difference (up to 100% of currency balance)
* If deposit is exceeding asset + currency the bot will also use 100% of currency balance held
* Deposit will be updated on each price change and new period to ensure the bot always only trades the amount as defined by --deposit
* Profit calculation will no longer go havoc as long as currency balance doesn't fall below whats currently deposited into the bot
* If enabled this will add two new columns to the output displaying the total currency balance (in green) as well as how much percentage (gray) of currency balance (0 - 100%) is currently used by the bot
* Works in Live, Paper & Sim mode

* Profit

* Fix profit calculation for stats and API
* Add deposit to Web GUI's capital row

* Profit starts at 100% on reset

* WebUI Print deposit only if enabled

* Add deposit to sim/exchange

* Less invasive profit fix

* Use deposit for profit/stats in sim if enabled

* Remove prev_trades dashboard sorting

* Restore prev_trades dashboard sorting

* Resolve upstream conflicts

* Update engine.test to match changes

As deposit no longer overrides buy_pct this is kinda expected...

* Update engine.test to match changes #2

With deposit set order would be below minimum with asset on hold

* Reset profit on deposit change
  • Loading branch information
defkev authored and DeviaVir committed Apr 23, 2018
1 parent 8ebae7e commit 0d0bfd3
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 89 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ zenbot trade --help
--asset_capital <amount> for paper trading, amount of start capital in asset
--avg_slippage_pct <pct> avg. amount of slippage to apply to paper trades
--buy_pct <pct> buy with this % of currency balance
--buy_max_amt <amt> buy with up to this amount of currency balance
--deposit <amt> absolute initial capital (in currency) at the bots disposal (previously --buy_max_amt)
--sell_pct <pct> sell with this % of asset balance
--markdown_buy_pct <pct> % to mark down buy price
--markup_sell_pct <pct> % to mark up sell price
Expand Down
2 changes: 1 addition & 1 deletion commands/sim.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ module.exports = function (program, conf) {
time: s.period.time
})
}
s.balance.currency = n(s.balance.currency).add(n(s.period.close).multiply(s.balance.asset)).format('0.00000000')
s.balance.currency = n(s.net_currency).add(n(s.period.close).multiply(s.balance.asset)).format('0.00000000')

s.balance.asset = 0
s.lookback.unshift(s.period)
Expand Down
13 changes: 9 additions & 4 deletions commands/trade.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = function (program, conf) {
.option('--asset_capital <amount>', 'for paper trading, amount of start capital in asset', Number, conf.asset_capital)
.option('--avg_slippage_pct <pct>', 'avg. amount of slippage to apply to paper trades', Number, conf.avg_slippage_pct)
.option('--buy_pct <pct>', 'buy with this % of currency balance', Number, conf.buy_pct)
.option('--buy_max_amt <amt>', 'buy with up to this amount of currency balance', Number, conf.buy_max_amt)
.option('--deposit <amt>', 'absolute initial capital (in currency) at the bots disposal (previously --buy_max_amt)', Number, conf.deposit)
.option('--sell_pct <pct>', 'sell with this % of asset balance', Number, conf.sell_pct)
.option('--markdown_buy_pct <pct>', '% to mark down buy price', Number, conf.markdown_buy_pct)
.option('--markup_sell_pct <pct>', '% to mark up sell price', Number, conf.markup_sell_pct)
Expand Down Expand Up @@ -82,6 +82,10 @@ module.exports = function (program, conf) {
so.debug = cmd.debug
so.stats = !cmd.disable_stats
so.mode = so.paper ? 'paper' : 'live'
if (so.buy_max_amt) {
console.log(('--buy_max_amt is deprecated, use --deposit instead!\n').red)
so.deposit = so.buy_max_amt
}

so.selector = objectifySelector(selector || conf.selector)
var engine = engineFactory(s, conf)
Expand Down Expand Up @@ -149,7 +153,7 @@ module.exports = function (program, conf) {

/* Implementing statistical Exit */
function printTrade (quit, dump, statsonly = false) {
var tmp_balance = n(s.balance.currency).add(n(s.period.close).multiply(s.balance.asset)).format('0.00000000')
var tmp_balance = n(s.net_currency).add(n(s.period.close).multiply(s.balance.asset)).format('0.00000000')
if (quit) {
if (s.my_trades.length) {
s.my_trades.push({
Expand Down Expand Up @@ -276,7 +280,7 @@ module.exports = function (program, conf) {
if(!shouldSaveStats) return

var output_lines = []
var tmp_balance = n(s.balance.currency).add(n(s.period.close).multiply(s.balance.asset)).format('0.00000000')
var tmp_balance = n(s.net_currency).add(n(s.period.close).multiply(s.balance.asset)).format('0.00000000')

var profit = s.start_capital ? n(tmp_balance).subtract(s.start_capital).divide(s.start_capital) : n(0)
output_lines.push('last balance: ' + n(tmp_balance).format('0.00000000').yellow + ' (' + profit.format('0.00%') + ')')
Expand Down Expand Up @@ -453,7 +457,7 @@ module.exports = function (program, conf) {
if (err) throw err
var prev_session = prev_sessions[0]
if (prev_session && !cmd.reset_profit) {
if (prev_session.orig_capital && prev_session.orig_price && ((so.mode === 'paper' && !raw_opts.currency_capital && !raw_opts.asset_capital) || (so.mode === 'live' && prev_session.balance.asset == s.balance.asset && prev_session.balance.currency == s.balance.currency))) {
if (prev_session.orig_capital && prev_session.orig_price && prev_session.deposit === so.deposit && ((so.mode === 'paper' && !raw_opts.currency_capital && !raw_opts.asset_capital) || (so.mode === 'live' && prev_session.balance.asset == s.balance.asset && prev_session.balance.currency == s.balance.currency))) {
s.orig_capital = session.orig_capital = prev_session.orig_capital
s.orig_price = session.orig_price = prev_session.orig_price
if (so.mode === 'paper') {
Expand Down Expand Up @@ -565,6 +569,7 @@ module.exports = function (program, conf) {
session.start_capital = s.start_capital
session.start_price = s.start_price
session.num_trades = s.my_trades.length
if (so.deposit) session.deposit = so.deposit
if (!session.orig_capital) session.orig_capital = s.start_capital
if (!session.orig_price) session.orig_price = s.start_price
if (s.period) {
Expand Down
1 change: 1 addition & 0 deletions extensions/exchanges/sim/exchange.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ module.exports = function sim (conf, s) {

getBalance: function (opts, cb) {
setTimeout(function() {
s.sim_asset = balance.asset
return cb(null, balance)
}, latency)
},
Expand Down
1 change: 1 addition & 0 deletions extensions/output/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ module.exports = function api () {

app.get('/', function (req, res) {
app.locals.moment = moment
app.locals.deposit = tradeObject.options.deposit
let datas = JSON.parse(JSON.stringify(objectWithoutKey(tradeObject, 'options'))) // deep copy to prevent alteration
res.render('dashboard', datas)
})
Expand Down
74 changes: 38 additions & 36 deletions lib/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ module.exports = function (s, conf) {
s.exchange.setFees({asset: s.asset, currency: s.currency})
}
if (so.mode === 'sim' || so.mode === 'paper') {
s.balance = {asset: so.asset_capital, currency: so.currency_capital}
s.balance = {asset: so.asset_capital, currency: so.currency_capital, deposit: 0}
}
else {
s.balance = {asset: 0, currency: 0}
s.balance = {asset: 0, currency: 0, deposit: 0}
}

function memDump () {
Expand All @@ -85,6 +85,7 @@ module.exports = function (s, conf) {
}

let asset_col_width = 0
let deposit_col_width = 0
let currency_col_width = 0
s.lookback = []
s.day_count = 1
Expand Down Expand Up @@ -211,20 +212,31 @@ module.exports = function (s, conf) {
}

function syncBalance (cb) {
let pre_asset = so.mode === 'sim' ? s.sim_asset : s.balance.asset
s.exchange.getBalance({currency: s.currency, asset: s.asset}, function (err, balance) {
if (err) return cb(err)
let diff_asset = n(pre_asset).subtract(balance.asset)
s.balance = balance
getQuote(function (err, quote) {
if (err) return cb(err)

let post_currency = n(diff_asset).multiply(quote.ask)
s.asset_capital = n(s.balance.asset).multiply(quote.ask).value()
let deposit = so.deposit ? Math.max(0, n(so.deposit).subtract(s.asset_capital)) : s.balance.currency // zero on negative
s.balance.deposit = n(deposit < s.balance.currency ? deposit : s.balance.currency).value()
if (!s.start_capital) {
s.start_price = n(quote.ask).value()
s.start_capital = n(s.balance.currency).add(n(s.balance.asset).multiply(quote.ask)).value()
s.start_capital = n(s.balance.deposit).add(s.asset_capital).value()
s.real_capital = n(s.balance.currency).add(s.asset_capital).value()
s.net_currency = s.balance.deposit

pushMessage('Balance ' + s.exchange.name.toUpperCase(), 'sync balance ' + s.start_capital + ' ' + s.currency + '\n')
if (so.mode !== 'sim') {
pushMessage('Balance ' + s.exchange.name.toUpperCase(), 'sync balance ' + s.real_capital + ' ' + s.currency + '\n')
}
} else {
s.net_currency = n(s.net_currency).add(post_currency).value()
}

s.asset_capital = n(s.balance.asset).multiply(quote.ask).value()
cb(null, { balance, quote })
})
})
Expand Down Expand Up @@ -352,7 +364,7 @@ module.exports = function (s, conf) {
}
if (is_reorder && s[signal + '_order']) {
if (signal === 'buy') {
reorder_pct = n(size).multiply(s.buy_order.price).add(s.buy_order.fee).divide(s.balance.currency).multiply(100)
reorder_pct = n(size).multiply(s.buy_order.price).add(s.buy_order.fee).divide(s.balance.deposit).multiply(100)
} else {
reorder_pct = n(size).divide(s.balance.asset).multiply(100)
}
Expand All @@ -372,22 +384,15 @@ module.exports = function (s, conf) {
} else {
buy_pct = so.buy_pct
}
if (so.buy_max_amt) { // account for held assets as buy_max
let adjusted_buy_max_amt = n(so.buy_max_amt).subtract(s.asset_capital).value()
if(adjusted_buy_max_amt < s.balance.currency){
let buy_max_as_pct = n(adjusted_buy_max_amt).divide(s.balance.currency).multiply(100).value()
buy_pct = buy_max_as_pct
}
}
if (so.use_fee_asset) {
fee = 0
} else if (so.order_type === 'maker' && (buy_pct + s.exchange.takerFee < 100 || !s.exchange.makerBuy100Workaround)) {
fee = s.exchange.makerFee
} else {
fee = s.exchange.takerFee
}
trade_balance = n(s.balance.currency).divide(100).multiply(buy_pct)
tradeable_balance = n(s.balance.currency).divide(100 + fee).multiply(buy_pct)
trade_balance = n(s.balance.deposit).divide(100).multiply(buy_pct)
tradeable_balance = n(s.balance.deposit).divide(100 + fee).multiply(buy_pct)
expected_fee = n(trade_balance).subtract(tradeable_balance).format('0.00000000', Math.ceil) // round up as the exchange will too
if (buy_pct + fee < 100) {
size = n(tradeable_balance).divide(price).format(s.product.asset_increment ? s.product.asset_increment : '0.00000000')
Expand Down Expand Up @@ -416,8 +421,8 @@ module.exports = function (s, conf) {
return cb(err)
}
}
if (n(s.balance.currency).subtract(s.balance.currency_hold || 0).value() < n(price).multiply(size).value() && s.balance.currency_hold > 0) {
msg('buy delayed: ' + formatPercent(n(s.balance.currency_hold || 0).divide(s.balance.currency).value()) + ' of funds (' + formatCurrency(s.balance.currency_hold, s.currency) + ') on hold')
if (n(s.balance.deposit).subtract(s.balance.currency_hold || 0).value() < n(price).multiply(size).value() && s.balance.currency_hold > 0) {
msg('buy delayed: ' + formatPercent(n(s.balance.currency_hold || 0).divide(s.balance.deposit).value()) + ' of funds (' + formatCurrency(s.balance.currency_hold, s.currency) + ') on hold')
return setTimeout(function () {
if (s.last_signal === signal) {
executeSignal(signal, cb, size, true)
Expand Down Expand Up @@ -726,14 +731,21 @@ module.exports = function (s, conf) {
let asset_col = n(s.balance.asset).format(s.asset === 'BTC' ? '0.00000' : '0.00000000') + ' ' + s.asset
asset_col_width = Math.max(asset_col.length + 1, asset_col_width)
process.stdout.write(z(asset_col_width, asset_col, ' ').white)
let currency_col = n(s.balance.currency).format(isFiat() ? '0.00' : '0.00000000') + ' ' + s.currency
currency_col_width = Math.max(currency_col.length + 1, currency_col_width)
process.stdout.write(z(currency_col_width, currency_col, ' ').yellow)
let consolidated = n(s.balance.currency).add(n(s.period.close).multiply(s.balance.asset)).value()
let profit = (consolidated - orig_capital) / orig_capital
let deposit_col = n(s.balance.deposit).format(isFiat() ? '0.00' : '0.00000000') + ' ' + s.currency
deposit_col_width = Math.max(deposit_col.length + 1, deposit_col_width)
process.stdout.write(z(deposit_col_width, deposit_col, ' ').yellow)
if (so.deposit) {
let currency_col = n(s.balance.currency).format(isFiat() ? '0.00' : '0.00000000') + ' ' + s.currency
currency_col_width = Math.max(currency_col.length + 1, currency_col_width)
process.stdout.write(z(currency_col_width, currency_col, ' ').green)
let circulating = s.balance.currency > 0 ? n(s.balance.deposit).divide(s.balance.currency) : n(0)
process.stdout.write(z(8, n(circulating).format('0.00%'), ' ').grey)
}
let consolidated = n(s.net_currency).add(n(s.balance.asset).multiply(s.period.close))
let profit = n(consolidated).divide(orig_capital).subtract(1).value()
process.stdout.write(z(8, formatPercent(profit), ' ')[profit >= 0 ? 'green' : 'red'])
let buy_hold = s.period.close * (orig_capital / orig_price)
let over_buy_hold_pct = (consolidated - buy_hold) / buy_hold
let buy_hold = n(orig_capital).divide(orig_price).multiply(s.period.close)
let over_buy_hold_pct = n(consolidated).divide(buy_hold).subtract(1).value()
process.stdout.write(z(8, formatPercent(over_buy_hold_pct), ' ')[over_buy_hold_pct >= 0 ? 'green' : 'red'])
}
if (!is_progress) {
Expand All @@ -749,16 +761,6 @@ module.exports = function (s, conf) {
if (so.mode !== 'live')
s.exchange.processTrade(trade)

if (so.mode !== 'live' && !s.start_capital) {
s.start_capital = 0
s.start_price = trade.price
if (so.asset_capital) {
s.start_capital += so.asset_capital * s.start_price
}
if (so.currency_capital) {
s.start_capital += so.currency_capital
}
}
if (!so.manual) {
executeStop()

Expand Down Expand Up @@ -805,7 +807,7 @@ module.exports = function (s, conf) {
}
syncBalance(function () {
let on_hold
if (type === 'buy') on_hold = n(s.balance.currency).subtract(s.balance.currency_hold || 0).value() < n(order.price).multiply(order.remaining_size).value()
if (type === 'buy') on_hold = n(s.balance.deposit).subtract(s.balance.currency_hold || 0).value() < n(order.price).multiply(order.remaining_size).value()
else on_hold = n(s.balance.asset).subtract(s.balance.asset_hold || 0).value() < n(order.remaining_size).value()

if (on_hold && s.balance.currency_hold > 0) {
Expand Down Expand Up @@ -965,7 +967,7 @@ module.exports = function (s, conf) {
z(10, 'VOL', ' ').grey,
z(8, 'RSI', ' ').grey,
z(32, 'ACTIONS', ' ').grey,
z(25, 'BAL', ' ').grey,
z(so.deposit ? 38 : 25, 'BAL', ' ').grey,
z(22, 'PROFIT', ' ').grey
].join('') + '\n')
},
Expand Down
12 changes: 10 additions & 2 deletions templates/dashboard.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,21 @@
<div class="white-box analytics-info">
<h3 class="box-title">Capital</h3>
<div class="row">
<div class="col-lg-6 col-12">
<div class="col-lg-<% if (typeof deposit != 'undefined') { %>4<% } else { %>6<% } %> col-12">
<ul class="list-inline two-part">
<li class="text-left" style="display:inline"><i class="ti-arrow-up text-purple"></i> <span class="text-purple">Asset</span></li>
<li class="text-left" style="display:inline"><i class="ti-arrow-up text-purple"></i> <span><%= new Intl.NumberFormat("en-US", {useGrouping: false, minimumFractionDigits: 2, maximumFractionDigits: 8}).format(balance.asset) %></span> <small><%= asset.toUpperCase() %></small></li>
</ul>
</div>
<div class="col-lg-6 col-12">
<% if (typeof deposit != "undefined") { %>
<div class="col-lg-4 col-12">
<ul class="list-inline two-part">
<li class="text-left" style="display:inline"><i class="ti-arrow-up text-purple"></i> <span class="text-purple">Deposit</span></li>
<li class="text-left" style="display:inline"><i class="ti-arrow-up text-purple"></i> <span><%= new Intl.NumberFormat("en-US", {useGrouping: false, minimumFractionDigits: 2, maximumFractionDigits: 8}).format(balance.deposit) %></span> <small><%= currency.toUpperCase() %></small></li>
</ul>
</div>
<% } %>
<div class="col-lg-<% if (typeof deposit != 'undefined') { %>4<% } else { %>6<% } %> col-12">
<ul class="list-inline two-part">
<li class="text-left" style="display:inline"><i class="ti-arrow-up text-purple"></i> <span class="text-purple">Currency</span></li>
<li class="text-left" style="display:inline"><i class="ti-arrow-up text-purple"></i> <span><%= new Intl.NumberFormat("en-US", {useGrouping: false, minimumFractionDigits: 2, maximumFractionDigits: 8}).format(balance.currency) %></span> <small><%= currency.toUpperCase() %></small></li>
Expand Down
Loading

2 comments on commit 0d0bfd3

@voravitl
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you make bot do multi buy ?
bot it can buy . It just only one . it can't do it the same time

DEBUG this below when other bot session command buy . (Other session status buying )

2018-05-03 09:46:23 - preparing buy order over 2372.00000000 XVG of 0.0199739 BTC (100%) tradeable balance with a expected fee of 0.00001998 BTC (0.1%)

2018-05-03 09:46:23 - buy delayed: +49.93% of funds (0.0099836 BTC) on hold

@narentin
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm running a simulation in maker (fee = 0%) and without --deposit option.
At the end of the simulation, the end balence is different before and after this commit (101.69 against 102.65).

Is it intentional?
What is the difference between s.net_currency, s.balance.currency and s.balance.deposit?

Please sign in to comment.