
Xin chào mọi người, tôi là “Oak Quantitative”. Bởi vì lời nhắc nhở về xu hướng thị trường mà tôi đã phát triển cách đây một thời gian [Theo dõi bảng điều khiển】 được mọi người yêu thích rộng rãi và có lời nhắc đồng thời từ tài khoản dịch vụ cùng tên [Oak Quantitative], cung cấp cho các nhà đầu tư mới và cũ một tài liệu tham khảo mới để đánh giá xu hướng thị trường. Tận dụng sự phổ biến này, chúng tôi bắt đầu kết nối với API mở rộng của FMZ để thực hiện giao tiếp tin nhắn giữa các robot và trực tiếp gửi cảnh báo thị trường đến các robot được chỉ định. Bài viết này đưa ra hai tình huống ứng dụng làm ví dụ để kích thích thảo luận. Tôi hy vọng bạn có thể phát triển thêm nhiều điều thú vị hơn nữa…
Bài viết này chủ yếu giới thiệu: 1. Các nhà phát triển kết nối với API mở rộng của FMZ thông qua ngôn ngữ JS như thế nào? (Bài viết này lấy phương thức GetNodeList làm ví dụ) 2. Trường hợp 1: Sử dụng phương thức CommandRobot của API mở rộng để triển khai giao tiếp tin nhắn giữa robot giám sát và các robot khác. 3. Trường hợp 2: Sử dụng phương thức GetRobotDetail của API mở rộng để đạt được mục tiêu giám sát và hiển thị thống nhất dữ liệu của nhiều robot.
Đăng ký AccessKey và SecretKey (sau đây gọi là AK và SK).
Chúng ta vào menu [Cài đặt tài khoản] -> [Giao diện API] -> [Tạo ApiKey mới] trên trang web chính thức của FMZ, sau đó lấy một bộ AK và SK và ghi lại. (AK và SK của FMZ không giống như các sàn giao dịch mà chúng chỉ hiển thị lần đầu tiên khi được tạo. Trong FMZ, chúng ta có thể xem toàn bộ dữ liệu của AK và SK bất kỳ lúc nào trong menu [Giao diện API])

Phát triển theo tài liệu của API mở rộng Đầu tiên, chúng ta hãy xem các bước chính để yêu cầu API
Giao diện API FMZ:
https://www.fmz.com/api/v1
{
'version' : '1.0', //自定义版本号
'access_key': '8a148320e0bxxxxxxxxxxxxxx19js99f', //AK
'method' : 'GetNodeList', //具体调用的方法
'args' : [], //具体method算法的参数列表
'nonce' : 1516292399361, //时间戳,单位毫秒
'sign' : '085b63456c93hfb243a757366600f9c2' //签名(需要根据上面5个参数加密获取,下面有讲)
}
以GetNodeList方法为例
https://www.fmz.com/api/v1?
access_key=8a148320e0bxxxxxxxxxxxxxx19js99f&
nonce=1516292399361&
args=%5B%5D&
sign=085b63456c93hfb243a757366600f9c2&
version=1.0&
method=GetNodeList
按照如下顺序进行参数拼接后,使用MD5加密算法加密字符串,并转换为十六进制数据字符串值,该值作为参数sign的值。
version + "|" + method + "|" + args + "|" + nonce + "|" + secretKey
var URL = "https://www.fmz.com/api/v1?";
var AK = "b3a53d3XXXXXXXXXXXXXXXXXXX866fe5";//这里替换成你自己的AccessKey
var SK = "1d9ddd7XXXXXXXXXXXXXXXXXXX85be17";//这里替换成你自己的SecretKey
function main() {
//获取5个基础参数对象
var param = getParam("1.0.0",AK,getArgs());
Log("param:",param);
//获取拼接参数md5加密后的结果
var md5Result = md5(param);
//赋值加密结果到基础参数对象中
param.sign = md5Result;
//获取请求api的URL
var finalUrl = getFinalUrl(param);
Log("finalUrl:",finalUrl);
//执行请求并打印结果
var info = HttpQuery(finalUrl);
Log("info:",info);
}
//获取基础5个参数的对象
function getParam(version,ak,args){
return {
'version': version,
'access_key': ak,
'method': 'GetNodeList',
'args': JSON.stringify(args),
'nonce': new Date().getTime()
}
}
//执行md5加密
function md5(param){
var paramUrl = param.version+"|"+param.method+"|"+param.args+"|"+param.nonce+"|"+SK
Log("paramUrl:",paramUrl);
return Hash("md5", "hex", paramUrl)
}
//获取最终请求URL
function getFinalUrl(param){
return URL+"access_key="+AK+"&nonce="+param.nonce+"&args="+param.args+"&sign="+param.sign+"&version="+param.version+"&method="+param.method;
}
//js中不支持...args的命名方式,所以改用arguments关键字获取参数数组
function getArgs(){
return [].slice.call(arguments);
}
Dựa trên đoạn mã trên, chúng ta hãy sử dụng phương thức CommandRobot để triển khai giao tiếp tin nhắn giữa các robot.
Đầu tiên, chúng ta hãy xem xét hai tham số cần thiết của phương thức CommandRobot(RobotId, Cmd).
| Tên tham số | kiểu | nghĩa |
|---|---|---|
| RobotId | int | ID robot, có thể lấy được bằng cách sử dụng GetRobotList(…) hoặc trên trang chi tiết robot |
| Cmd | String | Tin nhắn cho bot |
Biết được ý nghĩa của các tham số, chúng ta hãy triển khai phương thức gọi này tiếp theo.
Nhận ID robot trên trang chi tiết robot:

Triển khai phương pháp lấy thông điệp Cmd
//获取消息头信息
function getMessageBody(toUserName,msgType,content){
return ({
"toUserName":toUserName,//发送给谁
"fromUserName":AOKE_INFO,//消息来源
"createTime": new Date().getTime(),//当前时间戳
"msgType":msgType,//消息类型
"content":content,//消息内容
"msgId":Math.random().toString(36).slice(-8)//消息ID
})
}
//获取消息体趋势信息(消息头content字段的数据)
function getCtaDate(symbol,timeCycle,direction,nowCycleTime){
return {
"symbol":symbol,//交易币种
"timeCycle":timeCycle,//趋势周期
"direction":direction,//当前进入的方向,0:看空,1:看多
"createTime":new Date().getTime(),//当前时间戳
"nowCycleTime":nowCycleTime//当前进入的周期起始时间
}
}
//发送消息前先获取消息
var sendMessage = getMessageBody("测试对象",'CTARemind',getCtaDate('BTC_USDT','120','0','2020-05-1620:00:00'));
//把机器人ID和消息体通过getArgs()方法获取,并传入基础参数。
var param = getParam("1.0.0",AK,getArgs(17777,sendMessage));
function main(){
while(true) {
var cmd = GetCommand()
if (cmd) {
Log(cmd)
}
Sleep(1000)
}
}
Tin nhắn đã được gửi thành công:
Nhận tin nhắn thành công:

Tương tự như vậy, trước tiên chúng ta hãy xem xét các mô tả tham số của hai phương pháp GetRobotList(offset, length, robotStatus, label):
| Tên tham số | kiểu | nghĩa |
|---|---|---|
| offset | int | Số trang truy vấn |
| length | int | Độ dài dữ liệu trang truy vấn |
| robotStatus | int | Vượt qua -1 để có được tất cả |
| label | String | Thẻ tùy chỉnh có thể lọc ra tất cả các robot có thẻ này |
GetRobotDetail(RobotId):
| Tên tham số | kiểu | nghĩa |
|---|---|---|
| RobotId | int | ID Robot |
//获取机器人列表信息
var robotListJson = getAPIInfo('GetRobotList',getArgs(OFF_SET,PAGE_LENGTH,-1));
var robotList = robotListJson.data.result.robots;
//获取机器人详情信息
var robotDetailJson = getAPIInfo('GetRobotDetail',getArgs(robotId));
var robotDetail = robotDetailJson.data.result.robot;
function getLogPrient(infoArr){
return table = {
type: 'table',
title: '奥克量化的机器人展示',
cols: ['机器人ID','机器人名称','策略名称','下次扣费时间','已经消耗时间ms','已经消耗金额CNY','最近活跃时间','是否公开'],
rows: infoArr
};
}
var URL = "https://www.fmz.com/api/v1?";
var AK = "b3a53d3XXXXXXXXXXXXXXXXXXX866fe5";//这里替换成你自己的AccessKey
var SK = "1d9ddd7XXXXXXXXXXXXXXXXXXX85be17";//这里替换成你自己的SecretKey
var OFF_SET = 0;//查询的页码下标
var PAGE_LENGTH = 5;//查询页的数据长度
function main() {
LogReset();
while(true){
//获取机器人列表信息
var robotListJson = getAPIInfo('GetRobotList',getArgs(OFF_SET,PAGE_LENGTH,-1));
//取出机器人列表信息
var robotList = robotListJson.data.result.robots;
//创建展示机器人信息的数组
var infoArr = new Array();
var infoArr_index = 0;
for (index = 0; index < robotList.length; index++) {
var robot = robotList[index];
//取出当前循环到的机器人ID
var robotId = robot.id;
//获取机器人详情信息
var robotDetailJson = getAPIInfo('GetRobotDetail',getArgs(robotId));
var robotDetail = robotDetailJson.data.result.robot;
//转换详情为数组对象
var arr = getLogPrientItem(robotDetail);
infoArr[infoArr_index] = arr;
infoArr_index++;
}
Log("infoArr:",infoArr);
LogStatus('`' + JSON.stringify(getLogPrient(infoArr)) + '`');
Sleep(30000);
}
}
function getLogPrient(infoArr){
return table = {
type: 'table',
title: '奥克量化的机器人展示',
cols: ['机器人ID','机器人名称','策略名称','下次扣费时间','已经消耗时间ms','已经消耗金额CNY','最近活跃时间','是否公开'],
rows: infoArr
};
}
//通过参数获取API信息
function getAPIInfo(method,dateInfo){
//获取5个基础参数对象
var param = getParam("1.0.0",AK,method,dateInfo);
//Log("param:",param);
//获取拼接参数md5加密后的结果
var md5Result = md5(param);
//赋值加密结果到基础参数对象中
param.sign = md5Result;
//获取请求api的URL
var finalUrl = getFinalUrl(param);
//Log("finalUrl:",finalUrl);
//执行请求并打印结果
var info = HttpQuery(finalUrl);
//Log("info:",info);
return JSON.parse(info);
}
//获取基础5个参数的对象
function getParam(version,ak,method,args){
return {
'version': version,
'access_key': ak,
'method': method,
'args': JSON.stringify(args),
'nonce': new Date().getTime()
}
}
//执行md5加密
function md5(param){
var paramUrl = param.version+"|"+param.method+"|"+param.args+"|"+param.nonce+"|"+SK
//Log("paramUrl:",paramUrl);
return Hash("md5", "hex", paramUrl)
}
//获取最终请求URL
function getFinalUrl(param){
return URL+"access_key="+AK+"&nonce="+param.nonce+"&args="+param.args+"&sign="+param.sign+"&version="+param.version+"&method="+param.method;
}
//js中不支持...args的命名方式,所以改用arguments关键字获取参数数组
function getArgs(){
return [].slice.call(arguments);
}
//获取展示详情对象'机器人ID','机器人名称','策略名称','下次扣费时间','已经消耗时间ms','已经消耗金额CNY','最近活跃时间','是否公开'],
function getLogPrientItem(robotDetail){
var itemArr = new Array();
var iteArr_index = 0;
itemArr[iteArr_index++] = robotDetail.id;
itemArr[iteArr_index++] = robotDetail.name;
itemArr[iteArr_index++] = robotDetail.strategy_name;
itemArr[iteArr_index++] = robotDetail.charge_time;
itemArr[iteArr_index++] = robotDetail.charged;
itemArr[iteArr_index++] = robotDetail.consumed/1e8;
itemArr[iteArr_index++] = robotDetail.refresh;
itemArr[iteArr_index++] = robotDetail.public == 0?"已公开":"未公开";
return itemArr;
}

Trong quá trình mở rộng thực tế, có thể nhận ra nhiều chức năng thú vị hơn. Ví dụ, sử dụng phương pháp CommandRobot để cho phép mỗi robot gửi kiểm tra nhịp tim đến robot A. Nếu robot A phát hiện một máy không có nhịp tim nhưng robot vẫn đang chạy, nó có thể phát ra báo động thông qua số dịch vụ FMZ. Theo cách này, có thể tránh được các cảnh báo có thể khiến chương trình bị treo, chẳng hạn như vòng lặp chết _C(). Tôi hy vọng rằng thông qua nguồn cảm hứng của tôi lần này, nền tảng FMZ có thể có nhiều chức năng thú vị hơn nữa được mọi người phát triển và mở mã nguồn. Cuối cùng, tôi muốn cảm ơn nền tảng FMZ và tất cả những người tuyệt vời bao gồm ông Meng, ông Chao và ông Z vì sự hỗ trợ và giúp đỡ của họ. Cảm ơn~