Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Momentum based BTC trader
- # Copyright (C) Sam Pollard, 2017 - All Rights Reserved
- # Unauthorized copying of this file via any medium is strictly prohibited
- # Proprietary and confidential
- # Written by Sam Pollard <sam.d.pollard@gmail.com>, May 31, 2017.
- # Template code taken from https://cryptotrader.org/backtests/KohMmsLg5Y8NSzJJQ
- # If you're using this for margin trading, you must trade only with
- # Poloniex, specifically BTC/USDT, though Bitfinex may work and trades BTC/USD
- # The backtest should start with 100% of your starting account balance in BTC
- # This is meant for a period of 1 day.
- # Version 1, Backtest sent to Kory on May 30, 2017
- # https://cryptotrader.org/backtests/WABzEG7wFsXAYd6Yp
- # margin_trading and trading are mutually exclusive
- mt = require 'margin_trading' # May only be done with Binfinex or Poloniex
- # trading = require 'trading' # import core trading module
- talib = require 'talib' # import technical analysis library
- # Initialization method called before the script starts.
- # Context object holds script data and will be passed to 'handle' method.
- init: ->
- # note that init and handle methods don't need arguments
- # @data, @storage and @context can be accessed from anywhere in the code
- @context.OB = 90
- @context.OS = 25
- # This gets complaints at 0.003. For margin trading, it's
- # the amount of ETH (Poloniex) you trade. For trading,
- # it's the minimum %age of your assets you can order.
- @context.eps = 1e-6
- @context.invested = false # You start off all in BTC
- @context.open_short = 0.0
- @context.begin = true
- # This method is called for each tick
- handle: (context, data, storage)->
- # Poloniex: ETH/BTC
- # i.e. the instrument is eth, its price is per bitcoin
- instrument = data.instruments[0]
- price = instrument.price
- RSI = talib.RSI
- inReal: instrument.close
- startIdx: 0
- endIdx: instrument.close.length - 1
- optInTimePeriod: 2
- RSI_value = _.last(RSI)
- # TRADING
- # It appears cash and currency are the same thing
- #cash = @portfolio.positions[instrument.base()].amount
- #currency = @portfolio.positions[instrument.curr()].amount
- #assets = @portfolio.positions[instrument.asset()].amount
- # MARGIN TRADING
- info = mt.getMarginInfo instrument
- assets = info.margin_balance # The value of all trading assets in BTC (USD for Bitfinex)
- # debug "price = #{price}; assets = #{assets} btc; tradeable balance = #{info.tradable_balance} btc"
- cash = 0
- # Stretch and shift
- sf = 0.001
- plot
- RSI: RSI_value * sf - 100*sf
- OB: @context.OB * sf - 100*sf
- OS: @context.OS * sf - 100*sf
- # TODO: Normalize holdings to the price of 1 ETH
- # Holdings: cash + assets * price
- # Notes:
- # The portfolio object gives access to information about funds
- # instrument.curr() returns base asset id e.g cny
- # instrument.asset() returns traded asset id, for example: "btc"
- # MARGIN TRADING
- pos = mt.getPosition instrument
- # check if position is open
- #if pos
- # debug "position: #{pos.amount} @#{pos.price}"
- # price = pos.price
- try
- if @context.invested && RSI_value < @context.OS
- if mt.sell instrument, 'limit', 0.99*info.tradable_balance/price, price, instrument.interval * 60
- pos = mt.getPosition instrument
- debug "Short sell; new position: #{pos.amount} #{instrument.asset()}"
- @context.invested = false
- @context.open_short = Math.abs(pos.amount)
- # addOrder takes single object as argument
- # takeProfitOrder = mt.addOrder
- # instrument: instrument
- # side: 'buy'
- # type: 'limit'
- # amount: amount
- # price: instrument.price * 0.4
- # @storage.takeProfitOrder = takeProfitOrder.id
- else if RSI_value > @context.OB && info.tradable_balance > @context.eps && !@context.invested
- if mt.buy instrument, 'limit', 0.99*@context.open_short, price, instrument.interval * 60
- pos = mt.getPosition instrument
- if pos
- debug "Buy; new position: #{pos.amount} #{instrument.asset()}"
- @context.invested = true
- catch e
- # the exception will be thrown if funds are not enough
- if e.errorType == "InsufficientFunds"
- error "insufficient funds: you have #{info.tradable_balance}"
- else
- throw e
- # TRADING
- # if RSI_value > @context.OB
- # if cash > @context.eps * instrument.price
- # # open long position
- # #trading.buy instrument, "market", cash * (1.0 - @context.eps) / instrument.price
- # info "Bought #{instrument.asset()} with #{cash} at #{instrument.price}"
- # else
- # if RSI_value < @context.OS && assets > @context.eps * instrument.price
- # #trading.sell instrument # close long position
- # info "Sold #{assets} #{instrument.asset()} at #{instrument.price}"
- # Unlike orders, open positions don't get cancelled when the bot is stopped
- # the below snippet can be used to programmatically close it
- onStop: ->
- instrument = @data.instruments[0]
- pos = mt.getPosition instrument
- if pos
- debug "Closing position"
- mt.closePosition instrument
- # this method is called upon restart
- onRestart: ->
- debug "Restart detected"
- # for example, we might need to cancel open orders
- for o in mt.getActiveOrders()
- mt.cancelOrder o
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement