Script Example: SMA Trader
AwooOOoo opened this issue · 1 comments
I've been tinkering with the scripting capabilities and wrote a simple trader and thought I would share it in case anyone is interested and for possible inclusion in the documentation as it uses many of the available features and helped me learn a number of qwerks of using the script engine.
SMA Trader
Trading cryptocurrencies (or any other financial market) involves substantial risk, and there is always the potential for loss. Your trading results may vary. No representation is being made that this algorithm will guarantee profits, or not result in losses from trading.
SMA Trader is a simple buy /sell trader based on Simple Moving Averages (SMA). The following are the input parameters for the script. If you are going to use this script please create these variables.
Name | Description | Default | Required |
---|---|---|---|
intervalSize | Interval (in minutes) to to take each sample | 5 | Yes |
fastAverageLength | Length of fast average. Needs to be smaller than slow average. | 12 | Yes |
slowAverageLength | Length of slow average. Needs to be larger than fast average | 26 | Yes |
startInTrade | Whether we are starting in (money in base) or out (money in quote) of the trade | false | Yes |
amount | Amount to be traded | 1 | Yes |
The intervalSize defines an interval at which a ticker is created internally to tracker the price at the defined interval. The fastAverageLength and slowAverageLength define the number of ticks from the ticker defined by the intervalSize. Since there is no current access to history data in the script it, you need to wait for the moving averages to fill up before they can be used. This time is the intervalSize * [fast|slow]AverageLength so since the defaults are 5, 12 & 26, this means you need to wait 5 minutes * 12 samples for the fast average and 5 minutes * 26 samples for the slow average so be careful what you set these to as it will take a while to be ready.
Here is an example with really short lengths which will have the system ready quickly for testing, but obviously won't be much use for trading.
Here is what you can expect from the Server Notifications. The trades themselves are highlighted and updates to the SMA values are provided at each interval.
Code
var precision = 2; // Accuracy of amounts to display
var quotes = {}; // Contains arrays of historical data to calculate the SMAs
var lastPrice; // The last price data received
var subscription; // Current Subscription Object
var interval; // Interval Object
/*
Initiates a trade
Improvement:When we have #983 implemented we can read the actual balance and not just have a fixed amount
*/
function trade(side) {
var precision = 4;
// Not a good implementation as it always assumes you have the same fixed amount available
var params;
var amount;
if (side === "BUY") {
amount = parameters.amount;
params = {
market: parameters.selectedCoin,
direction: BUY, // BUY is not quoted so I can't use a variable
price: lastPrice,
amount: amount
}
} else {
amount = parameters.amount * lastPrice;
params = {
market: parameters.selectedCoin,
direction: SELL, // SELL is not quoted so I can't use a variable
price: lastPrice,
amount: amount
}
}
if (side === "BUY") params.side = BUY;
else params.side = SELL;
notifications.info("Trade: " + side + "ing " + amount + " for " + lastPrice);
// Place the order on the exchange
trading.limitOrder(params);
}
/*
Calculates a moving average
Improvement: When #985 is addressed we won't have to manually create a SMA function
*/
function simpleMovingAverage(name, averageLength, price) {
var sma = 0;
if (!(name in quotes)) quotes[name] = [];
if (quotes[name].length < averageLength) {
quotes[name].push(price);
notifications.info("Filling the SMA(" + averageLength + "): " + quotes[name].length + "/" + averageLength)
sma = undefined;
} else {
quotes[name].shift();
quotes[name].push(price);
for(c = 0; c < quotes[name].length; c++) { sma += quotes[name][c]; }
sma /= averageLength;
notifications.info("SMA(" + averageLength + ") = " + sma.toFixed(2));
}
return sma;
}
/*
Update the moving averages and trade if conditions are met
*/
function updateSMAs() {
// Update the moving averages
var fast = simpleMovingAverage("fast", parameters.fastAverageLength, lastPrice);
var slow = simpleMovingAverage("slow", parameters.slowAverageLength, lastPrice);
if (slow !== undefined && fast !== undefined) {
if (fast > slow && state.persistent.get("inTrade") === "false") {
trade("BUY");
state.persistent.set("inTrade", "true");
} else if (fast < slow && state.persistent.get("inTrade") === "true") {
trade("SELL");
state.persistent.set("inTrade", "false");
}
}
}
/*
Executes when the script is started
*/
function start() {
notifications.info("SMA Trader starting ");
// Check our lengths are the right way around or the trades will be backwards
if (parameters.fastAverageLength > parameters.slowAverageLength) {
notifications.alert("The fast average needs to be smaller than the slow average. Aborting!!!");
return FAILURE_PERMANENT
}
// Initialize the starting trade state
if (parameters.startInTrade === "true") state.persistent.set("inTrade", "true");
else state.persistent.set("inTrade", "false");
// Setup a time to trigger at the requested interval
interval = setInterval(
function(event) {
updateSMAs();
},
Math.round(1000 * 60 * parameters.intervalSize)
)
subscription = events.setTick(
function(event) {
lastPrice = event.ticker().getLast();
},
parameters.selectedCoin
)
return RUNNING
}
/*
Executes when the script is stopped
*/
function stop() {
events.clear(subscription); // Stop the subscription
clearInterval(interval); // Clear the interval
}
If you think this is of value I can do a PR on the wiki. Thoughts?
Create a showcase example to test different aspects of the script engine. Submitted a PR in the Wiki
gruelbox/orko-wiki@master...AwooOOoo:master#diff-9539630766db4c6743745087f04d55bd