[TOC]
Thanks for using our platform. This is a basic instruction for beginners, for a full version of our API documentation, check on FMZ API. There are many obvious specs that aren’t covered in this tutorial left for you to discover.
After learning the entire tutorial, you will know how FMZ works and be able to write some basic strategies.
What is FMZ platform?
FMZ is an automated trading platform for cryptocurrency traders with support for many bitcoin/eth/altcoin exchange markets.
What can FMZ do for you?
You can learn how to write your bots(strategies) from our strategies’ square which contains lots of open source code, share your strategy’s code with others, ask for professional help any time, run your strategy on many exchanges, control your bot on website with computer or cellphone, sell your strategies if you want, communicate with many other auto-trading lovers in our group . In a word, FMZ is a perfect platform for those who want to do automated trading.
Which Cryptocurrency exchanges does FMZ support?
FMZ supports almost all exchanges that are popular, such as Binance
, Bitfinex
, Bitstamp
, OKEX
, Huobi
, Poloniex
, etc. you can also trade futures on OKEX
and BitMEX
. Check the full support list on API. You only need to write one strategy and run it on all exchanges without any changes.
What programming languages does FMZ support?
FMZ supports JavaScript, Python, C++ (JavaScript and Python are recommended) for coding your strategies. Benefiting from the completed languages supporting(not a custom language only can be used for one platform), you can improve your programming skills as well as learn to write strategies.
Is your API KEY safe?
Indeed. your API-KEYs are saved after encryption. Here is how it works.
https
.Current feature list:
To run a bot, you need to have a strategy, add an exchange, deploy a docker first. the docker is your strategy’s executor running on your own computer or server.
A quick look of the main page
Add an exchange
Add at https://www.fmz.com/m/add-platform, or click Platform
label.
Your access key and secret key can be applied at the cryptocurrency exchange. API-KEY is used to trade and get the private information from the exchange. We don’t save any API-KEY or passwords on our server.
You can register on FMZ’s simulated exchange and add it for testing.
deploy a docker
FMZ doesn’t run bots for you, you need to deploy a docker by yourself as the executor, which is more flexible and safe since our service doesn’t participate in running your bots. Although we also provide public docker, it should only be used for testing.
For windows, it’s quite easy, just follow the instruction on https://www.fmz.com/m/add-node
For Linux, you can rent a VPS on our website, which will deploy the docker automatically. Here are the steps to deploy on your own Linux server (recommended):
wget www.fmz.com/dist/robot_linux_amd64.tar.gz
, command not found? install first yum install wget -y
.tar -xzvf robot_linux_amd64.tar.gz
to unzip../robot -s node.fmz.com/xxxxx -p -p yourFMZpassword
, you should see something like 2018/07/05 05:04:10 Login OK, SID: 62086, PID: 7226, Name: host.localdomain
, which means everything is worked.
node.fmz.com/xxxxx
is unique to every user, find your own on https://www.fmz.com/m/add-node.ctrl + C
to stop the docker.nohup ./robot -s node.fmz.com/xxxxx -p yourFMZpassword &
to run in the background. this step can also be done by Screen
command.Write a strategy
You should write your own strategy or buy from the square. Here we will use a simple JavaScript strategy as a demo to show how to use edit page. The strategy can be copied from https://www.fmz.com/strategy/125482. This tutorial will not cover how to use JavaScript as you can find plenty of tutorials online.
Ctrl+S
on edit mode.Below is the full explanation of the strategy. In order to push the message to your phone, you need to bind the telegram to your account at https://www.fmz.com/m/account
/*
This strategy will send a message to your telegram when the price is higher or lower than
the set price.
All strategies must have a main function as the entrance.
*/
function main() {
//change symbol,will cover the default symbol which was set when start a bot.Currency is a strategy arguments
exchange.IO("currency", Currency)
var lastPushTime = 0 //the variable of last push timestamp.
while(true){ //run a infinite loop, which is the basic structure
//_C() function can retry the request automatically after failure. not necessary. var ticker = exchange.GetTicker() is ok.
var ticker = _C(exchange.GetTicker) // for information about GetTicker, check on https://fmz-docs.readthedocs.io/en/latest/code_Instruction/Market%20API.html#getticker
if(ticker.Last > UpPrice || ticker.Last < LowPrice){ //ticker.Last represents the last deal price
if(Date.now() - lastPushTime > 300*1000){ //only push once in 5 mins, Date.now() return ms.
lastPushTime = Date.now() //update lastPushTime
Log(Currency, 'Price is: ', ticker.Last, '@') //Log the price on the bot's page and sent the message. '@' in the end means push message
}
}
Log(Currency, 'Price is: ', ticker.Last) //just log the price
Sleep(Interval*1000) //check the last price again after Interval seconds
}
}
Run the bot
Finally, it’s time to run a bot.
On the Robot
page, click Add robot
, or visit https://www.fmz.com/m/add-robot directly to add a bot.
exchanges[0]
, exchanges[1]
Manage the bot
On the Robot
page, you can see the bot is running.
LogProfit()
, can be any number you want.Click the bot’s name to the bot page for more information:
This part will introduce some most commonly used API, for a full version of our API documentation, check on FMZ API. It is highly recommended for beginners to run the demo code on Debug page.
2.1 Log
Use : Log(msg)
Parameters : strings or numbers
Description : Log a message to robot log page.
Return : None
Demo:
function main() {
var msg = 'msg string'
Log(msg)
Log('hello', 'world', 123)
Log("red color message", "#FF0000")
Log("push this message to telegram!@") // won't push on debug page
}
2.2 GetTicker
Use : exchange.GetTicker()
Parameters : None
Description : Get the current market’s ticker.
Return :
{"Info:{}, "High":5226.69, "Low":5086.37,"Sell":5210.63, "Buy":5208.5, "Last":5208.51, "Volume":1703.1245, "OpenInterest":0, "Time":1554884195976}
Demo:
function main() {
var ticker = exchange.GetTicker()
Log(ticker)
Log('Last Price: ',ticker.Last, 'Bid Price: ', ticker.Buy)
}
2.3 GetDepth
Use : exchange.GetDepth()
Parameters : None
Description : Get the current market’s order book.
Return :
{
"Info":null,
"Asks":[
{"Price":5866.38,"Amount":0.068644},
{"Price":5866.39,"Amount":0.263985},
{"Price":5866.73,"Amount":0.05},
{"Price":5866.77,"Amount":0.05},
{"Price":5867.01,"Amount":0.15},
{"Price":5875.89,"Amount":0.05},
......
]
"Bids":[
{"Price":5865.13,"Amount":0.001898},
{"Price":5865,"Amount":0.085575},
{"Price":5864.15,"Amount":0.013053},
{"Price":5863.65,"Amount":0.016727},
{"Price":5863.51,"Amount":0.128906},
{"Price":5863.15,"Amount":0.2}
......
],
"Time":1530241857399
}
Demo:
function main() {
var depth = exchange.GetDepth()
Log(depth)
Log('Bid one: ', depth.Bids[0].Price, 'Ask one: ', depth.Asks[0].Price)
}
2.4 GetRecords
Use : exchange.GetRecords()
, exchange.GetRecords(Period)
Parameters :
Name | Type | Mandatory | Description |
---|---|---|---|
Period | global varble | No | Kline’s cycle, Optional Parameters, default K line cycle is set when start the robot. |
All possible parameters:PERIOD_M1
:1 minute,PERIOD_M5
:5mins,PERIOD_M15
:15mins,PERIOD_M30
:30mins,PERIOD_H1
:1h,PERIOD_D1
:1d.
Description : Get Kline/candlestick bars for current market.
Return :
[
{"Time":1526616000000,"Open":7995,"High":8067.65,"Low":7986.6,"Close":8027.22,"Volume":9444676.27669432},
{"Time":1526619600000,"Open":8019.03,"High":8049.99,"Low":7982.78,"Close":8027,"Volume":5354251.80804935},
{"Time":1526623200000,"Open":8027.01,"High":8036.41,"Low":7955.24,"Close":7955.39,"Volume":6659842.42025361},
......
]
Demo:
//A useful JavaScript example using Records to get a close array:
function main(){
var close = []
var records = exchange.GetRecords(PERIOD_H1)
Log('total bars: ', records.length)
for(var i=0;i<records.length;i++){
close.push(records[i].Close)
}
return close
}
2.5 GetAccount
Use : exchange.GetAccount()
Parameters : None
Description : Get account information
Return :
{
"Stocks":0.38594816,// free base asset
"FrozenStocks":0, //locked base asset
"Balance":542.858308,//free quote asset
"FrozenBalance":0 //locked quote asset
"Info":{} //the raw data
}
Demo:
//A useful JavaScript example of Log your account value for a certain trading pair:
function main(){
while(true){
var ticker = exchange.GetTicker()
var account = exchange.GetAccount()
var price = ticker.Buy
var stocks = account.Stocks + account.FrozenStocks
var balance = account.Balance + account.FrozenBalance
var value = stocks*price + balance
Log('Account value is: ', value)
LogProfit(value)
Sleep(3000)//sleep 3000ms(3s), A loop must has a sleep, or the rate-limit of the exchange will be exceed
//when run in debug tool, add a break here
}
}
2.6 Buy
Use : exchange.Buy(Price, Amount)
, exchange.Buy(Price, Amount, Msg)
Parameters :
Name | Type | Mandatory | Description |
---|---|---|---|
Price | Number | Yes | Buy price of limit order |
Amount | Number | Yes | Buy amount of limit order |
Msg | String | No | Add an extra message on Log page |
Description : Send a buy order and a buy log at bot’s page
Return : return the OrderID if success, null
if not.
Demo:
//A useful JavaScript example of Buy for buy certain amount of bitcoin at a certain price:
function main(){
while(true){
var ticker = exchange.GetTicker()
var price = ticker.Sell
if(price >= 7000){
exchange.Buy(price+5, 1, 'BTC-USDT')
}
Sleep(3000)//Sleep 3000ms
}
}
2.7 Sell
Use : exchange.Sell(Price, Amount)
, exchange.Sell(Price, Amount, Msg)
Parameters :
Name | Type | Mandatory | Description |
---|---|---|---|
Price | Number | Yes | Sell price of limit order |
Amount | Number | Yes | sell amount of limit order |
Msg | String | No | Add an extra message on Log page |
Description : Send a sell order and a sell log at bot’s page
Return : return the OrderID if success, null
if not.
Demo:
//A useful JavaScript example of Buy for buy certain amount of bitcoin at a certain price:
function main(){
while(true){
var ticker = exchange.GetTicker()
var price = ticker.Buy
if(price >= 7000){
var id = exchange.Sell(price-5, 1, 'BTC-USDT')
Log('OrderId: ', id)
}
Sleep(3000)
}
}
2.8 GetOrder
Use : exchange.GetOrder(OrderId)
Parameters :
Name | Type | Mandatory | Description |
---|---|---|---|
OrderId | Number | Yes | Order Id |
Description : Get order details by order id. Return :
{
"Id":125723661,
"Amount":0.01,
"Price":7000,
"DealAmount":0,
"AvgPrice":0,
"Status":0, // 0:Not filled, 1:Filled, 2:Canceled
"Type":1,// 0:Buy, 1:Sell
"ContractType":"",//just for futures contract orders
"Info":{} //raw info from exchange
}
}
Demo:
//A JavaScript example of using this API, which will buy until your account has 5 coins:
function main(){
while(true){
var amount = exchange.GetAccount().Stocks
var ticker = exchange.GetTicker()
var id = null
if(5-amount>0.01){
id = exchange.Buy(ticker.Sell, Math.min(10-amount,0.2))
}else{
Log('Job completed')
return //return the main function, bot will stop
}
Sleep(3000) //Sleep 3000ms
if(id){
var status = exchange.GetOrder(id).Status
if(Status == 0){
exchange.CancelOrder(id)
}
}
}
}
2.9 GetOrders
Use : exchange.GetOrders()
Parameters : None
Description : Get all open orders for your trading symbols.
Return : A list of open orders, the result has the same meanning as GetOrder()
[
{
"Info":{},
"Id":16387538,
"Amount":1123,
"Price":0.00012826,
"DealAmount":0,
"AvgPrice":0,
"Status":0,
"Type":1,
"ContractType":""
}
]
Demo:
//A JavaScript example of using this API, which will cancel all open orders for trading symbol:
fuction CancelAll(){
var orders = exchange.GetOrders()
for(var i=0;i<orders.length,i++){
exchange.CancelOrder(orders[[i].Id) // cancel order by orderID
}
}
function main(){
CancelAll()
while(true){
//do something
Sleep(10000)
}
}
2.10 CancelOrder
Use : exchange.CancelOrder(OrderId)
Parameters :
Name | Type | Mandatory | Description |
---|---|---|---|
OrderId | Number | Yes | Order Id |
Description : Cancel an order by order id.
Return : bool type, true
means that the cancellation of the order request was successful. false
means cancellation of the order request failed.
2.11 SetContractType
Use : exchange.SetContractType(ContractType)
Parameters :
Name | Type | Mandatory | Description |
---|---|---|---|
ContractType | String | Yes | ContractType |
Description : Set contract type for futures trade. must be set first before using other private API. Return : None Demo:
exchange.SetContractType("this_week") //OKEX future has “this_week”, “next_week”, “quarter” , "swap"
exchange.SetContractType("XBTUSD") //BitMEX future has "XBTUSD","XBTM19",etc
2.12 GetPosition
Use : exchange.GetPosition()
Parameters : None
Description : Get the current position information, only for futures trade.
Return : A List of Positions, will return empty list if the account has no position.
Demo:
// Note: GetPosition function obtains all positions.
function main(){
exchange.SetContractType("this_week") //for OKEX future
var position = exchange.GetPosition()
if(position.length>0){
Log("Amount:", position[0].Amount, "FrozenAmount:", position[0].FrozenAmount, "Price:",
position[0].Price, "Profit:", position[0].Profit, "Type:", position[0].Type, "ContractType:", position[0].ContractType)
}
}
2.13 SetDirection
Use : exchange.SetDirection(Direction)
Parameters :
Name | Type | Mandatory | Description |
---|---|---|---|
Direction | String | Yes | can be buy , closebuy , sell , closesell . |
Description : Set Buy or Sell Order Types, only for Futures trade. Return : None Demo:
function main(){
exchange.SetContractType("this_week");
exchange.SetMarginLevel(5) // Set the leverage to 5 times
exchange.SetDirection("buy") // Set the order type to buy long
exchange.Buy(5000, 2) //buy long at the price 1000, quantity of 2
exchange.SetDirection("closebuy")
exchange.Sell(4999, 2) //close long position
}
2.14 Other commonly used function:
Check more details about those functions on FMZ API Docs
Name | Description | Example |
---|---|---|
LogStatus |
Log a message or tables on bot’s status bar,will refresh every time | LogStatus('msg') |
_C |
Retry function | _C(exchange.GetRecords,PERIOD_H1) ,_C(exchange.GetTicker) |
_N |
Position function | _N(4001.512,2) ,_N(num,0) |
_G |
Global dictionary that can be saved after restart robot. | _G('initValue', 1000);_G('initValue') |
_D |
Returns the timestamp | _D() , _D(1478570053241) |
TA |
TA-Lib Indicator Library. support MACD , EMA , KDJ etc… |
TA.MACD(records) |
Math |
Supprot math fucntion,check on https://mathjs.org/ | Math.min(1,2) , Math.sqrt(2) |
There are lots of teaching strategy in https://www.fmz.com/square/s:tag:Study/1 , which is simple and easy for beginers.
This is a simple but powerful strategy that used to earn hundreds of times in real BTC spot markets. It can’t ve run on exchanges that have high trade fee.
var floatAmountBuy = 20
var floatAmountSell = 20
var diffPrice = 3
var Interval = 3000
function CancelPendingOrders() {
var orders = _C(exchange.GetOrders);
for (var j = 0; j < orders.length; j++) {
exchange.CancelOrder(orders[j].Id, orders[j])
}
}
function GetPrice(depth) {
var price = {buy:0, sell:0}
var askAmount = 0
var bidAmount = 0
for(var i=0; i<depth.Bids.length; i++){
askAmount += depth.Asks[i].Amount
bidAmount += depth.Bids[i].Amount
if(askAmount >= floatAmountBuy && !price.buy){
price.buy = depth.Asks[i].Price
}
if(bidAmount >= floatAmountSell && !price.sell){
price.sell = depth.Bids[i].Price
}
}
if(!price.buy || !price.sell){
price = {buy:depth.Asks[depth.Asks.length-1].Price, sell:depth.Bids[depth.Bids.length-1].Price}
}
return price
}
function onTick() {
var price = GetPrice(_C(exchange.GetDepth))
var buyPrice = price.buy + 0.01
var sellPrice = price.sell - 0.01
if ((sellPrice - buyPrice) <= diffPrice){
buyPrice -= 10
sellPrice += 10
}
CancelPendingOrders()
var account = _C(exchange.GetAccount)
var amountBuy = _N((account.Balance / buyPrice-0.01), 2)
var amountSell = _N((account.Stocks), 2)
if (amountSell > 0.02) {
exchange.Sell(sellPrice, amountSell)
}
if (amountBuy > 0.02) {
exchange.Buy(buyPrice, amountBuy)
}
}
function main() {
while (true) {
onTick()
Sleep(Interval)
}
}
A classic breakout strategy, Check on https://www.fmz.com/strategy/103247 for configs. You can learn how to trade features and draw charts from the source code.
var ChartCfg = {
__isStock: true,
title: {
text: 'Dual Thrust Up-Down Track'
},
yAxis: {
plotLines: [{value: 0,
color: 'red',
width: 2,
label: {
text: 'Up Track',
align: 'center'}
},
{value: 0,
color: 'green',
width: 2,
label: {
text: 'Down Track',
align: 'center'},
}
]
},
series: [{type: 'candlestick',
name: 'current cycle',
id: 'primary',
data: []
},
{type: 'flags',
onSeries: 'primary',
data: [],
}
]
};
var STATE_IDLE = 0;
var STATE_LONG = 1;
var STATE_SHORT = 2;
var State = STATE_IDLE;
var LastBarTime = 0;
var UpTrack = 0;
var BottomTrack = 0;
var chart = null;
var InitAccount = null;
var LastAccount = null;
var Counter = {
w: 0,
l: 0
};
function GetPosition(posType) {
var positions = exchange.GetPosition();
for (var i = 0; i < positions.length; i++) {
if (positions[i].Type === posType) {
return [positions[i].Price, positions[i].Amount];
}
}
return [0, 0];
}
function CancelPendingOrders() {
while (true) {
var orders = exchange.GetOrders();
for (var i = 0; i < orders.length; i++) {
exchange.CancelOrder(orders[i].Id);
Sleep(Interval);
}
if (orders.length === 0) {
break;
}
}
}
function Trade(currentState, nextState) {
var pfn = nextState === STATE_LONG ? exchange.Buy : exchange.Sell;
if (currentState !== STATE_IDLE) {
exchange.SetDirection(currentState === STATE_LONG ? "closebuy" : "closesell");
while (true) {
var amount = GetPosition(currentState === STATE_LONG ? PD_LONG : PD_SHORT)[1];
if (amount === 0) {
break;
}
// pfn(amount);
pfn(nextState === STATE_LONG ? _C(exchange.GetTicker).Sell * 1.001 : _C(exchange.GetTicker).Buy * 0.999, amount);
Sleep(Interval);
CancelPendingOrders();
}
var account = exchange.GetAccount();
if (account.Stocks > LastAccount.Stocks) {
Counter.w++;
} else {
Counter.l++;
}
LogProfit(_N(account.Stocks - InitAccount.Stocks), "Profit rate:", _N((account.Stocks - InitAccount.Stocks) * 100 / InitAccount.Stocks) + '%');
LastAccount = account;
}
exchange.SetDirection(nextState === STATE_LONG ? "buy" : "sell");
while (true) {
var pos = GetPosition(nextState === STATE_LONG ? PD_LONG : PD_SHORT);
if (pos[1] >= AmountOP) {
Log("Average Price", pos[0], "amount:", pos[1]);
break;
}
// pfn(AmountOP-pos[1]);
pfn(nextState === STATE_LONG ? _C(exchange.GetTicker).Sell * 1.001 : _C(exchange.GetTicker).Buy * 0.999, AmountOP-pos[1]);
Sleep(Interval);
CancelPendingOrders();
}
}
function onTick(exchange) {
var records = exchange.GetRecords();
if (!records || records.length <= NPeriod) {
return;
}
var Bar = records[records.length - 1];
if (LastBarTime !== Bar.Time) {
var HH = TA.Highest(records, NPeriod, 'High');
var HC = TA.Highest(records, NPeriod, 'Close');
var LL = TA.Lowest(records, NPeriod, 'Low');
var LC = TA.Lowest(records, NPeriod, 'Close');
var Range = Math.max(HH - LC, HC - LL);
UpTrack = _N(Bar.Open + (Ks * Range));
DownTrack = _N(Bar.Open - (Kx * Range));
if (LastBarTime > 0) {
var PreBar = records[records.length - 2];
chart.add(0, [PreBar.Time, PreBar.Open, PreBar.High, PreBar.Low, PreBar.Close], -1);
} else {
for (var i = Math.min(records.length, NPeriod * 3); i > 1; i--) {
var b = records[records.length - i];
chart.add(0, [b.Time, b.Open, b.High, b.Low, b.Close]);
}
}
chart.add(0, [Bar.Time, Bar.Open, Bar.High, Bar.Low, Bar.Close]);
ChartCfg.yAxis.plotLines[0].value = UpTrack;
ChartCfg.yAxis.plotLines[1].value = DownTrack;
ChartCfg.subtitle = {
text: 'Up Track: ' + UpTrack + ' Down Track: ' + DownTrack
};
chart.update(ChartCfg);
chart.reset(PeriodShow);
LastBarTime = Bar.Time;
} else {
chart.add(0, [Bar.Time, Bar.Open, Bar.High, Bar.Low, Bar.Close], -1);
}
LogStatus("Price:", Bar.Close, "Up:", UpTrack, "Down:", DownTrack, "Wins: ", Counter.w, "Losses:", Counter.l, "Date:", new Date());
var msg;
if (State === STATE_IDLE || State === STATE_SHORT) {
if (Bar.Close >= UpTrack) {
msg = 'Long Price: ' + Bar.Close + ' Up Track:' + UpTrack;
Log(msg);
Trade(State, STATE_LONG);
State = STATE_LONG;
chart.add(1, {x:Bar.Time, color: 'red', shape: 'flag', title: 'Long', text: msg});
}
}
if (State === STATE_IDLE || State === STATE_LONG) {
if (Bar.Close <= DownTrack) {
msg = 'Short Price: ' + Bar.Close + ' Down Track:' + DownTrack;
Log(msg);
Trade(State, STATE_SHORT);
chart.add(1, {x:Bar.Time, color: 'green', shape: 'circlepin', title: 'Short', text: msg});
State = STATE_SHORT;
}
}
}
function onexit() {
var pos = exchange.GetPosition();
if (pos.length > 0) {
Log("Warning, has positions when exiting", pos);
}
}
function main() {
if (exchange.GetName() !== 'Futures_OKCoin') {
throw "Only support OKEX features";
}
exchange.SetRate(1);
exchange.SetContractType(["this_week", "next_week", "quarter"][ContractTypeIdx]);
exchange.SetMarginLevel([10, 20][MarginLevelIdx]);
if (exchange.GetPosition().length > 0) {
throw "Can't have Positions when start.";}
CancelPendingOrders();
InitAccount = LastAccount = exchange.GetAccount();
LoopInterval = Math.min(1, LoopInterval);
Log('Exchange Name:', exchange.GetName(), InitAccount);
LogStatus("Ready...");
LogProfitReset();
chart = Chart(ChartCfg);
chart.reset();
LoopInterval = Math.max(LoopInterval, 1);
while (true) {
onTick(exchange);
Sleep(LoopInterval * 1000);
}
}
小草 keep updating on this post, feel free to ask any questions