- Get started to develop your Strategy Class by using Nodejs in compliance with ES6.
- There are dedicated interfaces to be coded along, please refer to 'Basic Coding Structure' as below; note that your strategy takes in current market trading data and must return results whether your trades are executed or not.
- The life cycle of your trades/orders are maintained by your own strategy.
- You can leverage off-the-shelf technical analysis indicators of TA-LIB for quickly develop your strategies.
- You can record your trading data with
Log(str)
.
class BuyOneSellOneMarket {
trade(information) {
Log("Start strategy!");
const exchange = Object.keys(information.candles)[0];
const pair = Object.keys(information.candles[exchange])[0];
if (this.lastOrderType === 'sell') {
this.lastOrderType = 'buy';
return [
{
exchange: exchange,
pair: pair,
type: 'MARKET',
amount: 1,
price: -1
}
]
} else {
this.lastOrderType = 'sell';
return [
{
exchange: exchange,
pair: pair,
type: 'MARKET',
amount: -1,
price: -1
}
]
}
}
// must have, monitoring life cycle of order you made
onOrderStateChanged(state) {
Log('onOrderStateChanged');
}
constructor() {
// must have for developer
this.subscribedBooks = {
'Bitfinex': {
pairs: [ 'BTC-USDT' ]
},
};
// seconds for broker to call trade()
// do not set the frequency below 60 sec.
// 60 * 30 for 30 mins
this.period = 60 * 30;
// must have
// assets should be set by broker
this.assets = undefined;
// customizable properties
// sell price - buy price
this.minEarnPerOp = 1;
// other properties, your code
this.lastOrderType = 'sell';
}
}
When a strategy is called, information
as a parameter is carried into trade()
, containing two categories of objects, candle
and orderBooks
. The following code snippet shows you how to retrieve candle
and orderBooks
info,
const candle = information.candle;
const orderBooks = information.orderBooks;
Crypto-Arsenal supports multi-pair trading, candle
and orderBooks
could carry-in data for multiple pairs. The following code snippet shows you how to acquaire one specific candle and orderbook info among multiple pairs in an exchange.
Please note that exchange
and pair
are registered by developers through this.subscribedBooks
in strategy constructor
.
const candle = information.candle;
const oneCandle = candle[exchange][pair];
const orderBooks = information.orderBooks;
const oneOrderBook = orderBooks[exchange][pair];
If a strategy is used for single-pair trading, the single trading pair and exchange can be assigned on the fly. The following code snippet shows you how to get current exchange
and pair
info.
// Correct way to get exchange / pair in single pair strategy
const exchange = Object.keys(information.candles)[0];
const pair = Object.keys(information.candles[exchange])[0];
const candleData = information.candles[exchange][pair][0];
Each candle
contains five different info, open
, close
, high
, low
and volume
with respect to a trading pair under an exchange. The following code snippet shows you how to acquire these info.
const candles = information.candles;
const oneCandle = candles[exchange][pair][0];
const open = oneCandle.open;
const close = oneCandle.close;
const high = oneCandle.high;
const low = oneCandle.low;
const volume = oneCandle.volume;
Each orderBook
contains two different info, asks
and bids
, with respect to a trading pair under an exchange. Each of asks
or bids
contains three different info, price
, count
and amount
. The following code snippet shows you how to acquire these info. Please note that there shows NO orderBooks
info under Backtesting Mode.
const orderBooks = information.orderBooks;
const oneOrderBook = orderBooks[exchange][pair];
const askOrderBook = oneOrderBook.asks;
for (const askOrder of askOrderBook) {
const price = askOrder.price;
const count = askOrder.count;
const amount = askOrder.amount;
}
const bidOrderBook = oneOrderBook.bids;
for (const bidOrder of bidOrderBook) {
const price = bidOrder.price;
const count = bidOrder.count;
const amount = bidOrder.amount;
}
The following code snippet shows you how to get your up-to-date assets in an exchange.
const usd = this.assets[exchange]['USDT'];
const eth = this.assets[exchange]['ETH'];
All strategies in Crypto-Arsenal, the return value from trade()
contains 0 to N objects in an array, shown as below code snippet. You make orders to an exchange by this return value.
[
{
exchange: string
pair: string
type: string
amount: float
price: float
},
{
exchange: string
pair: string
type: string
amount: float
price: float
} ....
]
- exchange: The exchage you would like to put orders to.
- pair: Your assigned trading pair.
- type: The type of trading,
LIMIT
: order is made by assigned price;MARKET
: order is made by market price. - amount: The amount of crypto for trading. '>= 0' means 'buy' (order taker). '< 0' means 'sell' (order maker).
- price: The price you wish for trading. If 'type' is assigned to
MARKET
, then price can be any value.
In the following code snippet, it shows that this order is made to 'Binance' with assigned price of 212.42 USDT to purpose 1 ETH.
[
{
exchange: 'Binance',
pair: 'ETH-USDT',
type: 'LIMIT',
amount: 1,
price: 212.42
}
]
In the following code snippet, it shows that this order is made to 'Binance' with current market price (USDT) to purpose 1 ETH. Note: It is mandatory that price shalle be assigned to any value, even the type of trading is based on market price!
[
{
exchange: 'Binance',
pair: 'ETH-USDT',
type: 'MARKET',
amount: 1,
price: -1
}
]
Used to log the message. 'str' must be javescript string type and maximum length of 'str' is 200 characters.
Log(str);
Use this.xxx
or this['OPTION_NAME']
to access parameter of a strategy.
The following example is a MA Cross Strategy, which uses TA-LIB to calculate two moving averages, short and long lines. When a signal ('short' breaks through 'long' line) appears, then it shows a 'buying point'; otherwise, 'selling point'.
class EMACross {
// must have
// function for trading strategy
// return order object for trading or null for not trading
trade(information) {
// define your own trading strategy here
// exchange may offline
if (!information.candles) return [];
const exchange = Object.keys(information.candles)[0];
const pair = Object.keys(information.candles[exchange])[0];
const baseCurrency = pair.split('-')[1]; // pair must in format '{TARGET}-{BASE}', eg. BTC-USDT, ETH-BTC
const currency = pair.split('-')[0]; // pair must in format '{TARGET}-{BASE}', eg. BTC-USDT, ETH-BTC
if (!information.candles[exchange][pair]) return [];
// information like
const candleData = information.candles[exchange][pair][0];
// keep track history data
this.history.push({
Time: candleData.time,
Open: candleData.open,
Close: candleData.close,
High: candleData.high,
Low: candleData.low,
Volumn: candleData.volumn,
});
let lastPrice = (information.candles[exchange][pair][0]['close']);
if (!lastPrice) return [];
// release old data
if (this.history.length > this.long) {
this.history.shift();
} else {
return [];
}
const marketData = this.history;
// Calculate EMA with TA-LIB, return type is [double], get last MA value by pop()
const MAShort = TA.EMA(marketData, 'Close', this.short).pop();
const MALong = TA.EMA(marketData, 'Close', this.long).pop();
// Track cross
const curSide = (MAShort > MALong) ? ('UP') : ('DOWN');
Log("MA side: " + curSide);
if(!this.preSide) {
this.preSide = curSide;
return [];
}
// When up cross happend
if (this.phase == this.PHASES.waitBuy && this.preSide == 'DOWN' && curSide == 'UP') {
// Not enough assets, we can't buy
if (this.assets[exchange][baseCurrency] < lastPrice) {
return [];
}
this.preSide = curSide;
this.phase = this.PHASES.waitSell;
// Buy 1 coin
return [
{
exchange: exchange,
pair: pair,
type: 'LIMIT',
amount: 1, // [CHANGE THIS] Buying Amount
price: lastPrice
}
];
}
if (this.phase == this.PHASES.waitSell && this.preSide == 'UP' && curSide == 'DOWN') {
this.preSide = curSide;
this.phase = this.PHASES.waitBuy;
// Sell all remaining coin
return [
{
exchange: exchange,
pair: pair,
type: 'LIMIT',
amount: -this.assets[exchange][currency], // All Out
price: lastPrice
}
];
}
this.preSide = curSide;
return [];
}
// must have
onOrderStateChanged(state) {
Log('order change' + JSON.stringify(state));
}
constructor() {
// must have for developer
this.subscribedBooks = {
'Binance': {
pairs: ['ETH-USDT']
},
};
// seconds for broker to call trade()
// do not set the frequency below 60 sec.
this.period = 60 * 60; // [CHANGE THIS] Set time period for MA lines
// must have
// assets should be set by broker
this.assets = undefined;
// customizable properties
this.long = 10; // [CHANGE THIS] Set MA Cross period for long line
this.short = 5; // [CHANGE THIS] Set MA Cross period for short line
this.PHASES = {
init: 0,
waitBuy: 2,
waitSell: 4
};
this.preSide = undefined;
this.phase = this.PHASES.waitBuy;
this.history = [];
}
}