量化取引プラットフォームの発明者は,取引所や通貨が非常に限られ,取引モデルも多くなかった時期が早すぎたため,初期APIの設計は比較的単純で,単一通貨取引戦略に焦点を当てた. 繰り返し,特に最近のバージョンが,比較的完善して,通常使用される取引所のAPIは,すべての機能で完成した. 特に多通貨戦略,取引,口座,取引を入手する方法は,以前よりもはるかに簡素化され,IO機能にアクセスするAPIインターフェースは,もはや必要ではない.
管理者は完全なサポートのために3.7にアップグレードする必要があります.APIインターフェースの追加機能などの情報は,発明者の量化取引プラットフォームのAPIドキュメントに同期して更新されています:
文法手帳:https://www.fmz.com/syntax-guideユーザーガイド:https://www.fmz.com/user-guide
現在,APIには統一された取得精度関数があり,これは永続契約の例として紹介されています.
//全局的变量,储存数据,SYMBOLS代表要交易的币种,格式如"BTC,ETH,LTC", QUOTO为基础货币,永续合约常见的有USDT,USDC,INTERVAL代表循环的间隔。
var Info = { trade_symbols: SYMBOLS.split(","), base_coin: QUOTO, ticker: {}, order: {}, account: {}, precision: {},
position: {}, time:{}, count:{}, interval:INTERVAL}
function InitInfo() {
//初始化策略
if (!IsVirtual() && Version() < 3.7){
throw "[trans]FMZ平台升级API,需要下载最新托管者|Update to neweset docekr[/trans]";
}
Info.account = {init_balance:0};
Info.time = {
update_ticker_time: 0,
update_pos_time: 0,
update_profit_time: 0,
update_account_time: 0,
update_status_time: 0,
last_loop_time:0,
loop_delay:0,
};
for (let i = 0; i < Info.trade_symbols.length; i++) {
let symbol = Info.trade_symbols[i];
Info.ticker[symbol] = { last: 0, ask: 0, bid: 0 };
Info.order[symbol] = { buy: { id: 0, price: 0, amount: 0 }, sell: { id: 0, price: 0, amount: 0 } };
Info.position[symbol] = { amount: 0, hold_price: 0, unrealised_profit: 0, open_time: 0, value: 0 };
Info.precision[symbol] = {};
}
}
//获取精度
function GetPrecision() {
let exchange_info = exchange.GetMarkets();
for (let pair in exchange_info) {
let symbol = pair.split('_')[0]; //永续合约交易对的格式为 BTC_USDT.swap
if (Info.trade_symbols.indexOf(symbol) > -1 && pair.split('.')[0].endsWith(Info.base_coin) && pair.endsWith("swap")) {
Info.precision[symbol].tick_size = exchange_info[pair].TickSize;
Info.precision[symbol].amount_size = exchange_info[pair].AmountSize;
Info.precision[symbol].price_precision = exchange_info[pair].PricePrecision
Info.precision[symbol].amount_precision = exchange_info[pair].AmountPrecision
Info.precision[symbol].min_qty = exchange_info[pair].MinQty
Info.precision[symbol].max_qty = exchange_info[pair].MaxQty
Info.precision[symbol].min_notional = exchange_info[pair].MinNotional
Info.precision[symbol].ctVal = exchange_info[pair].CtVal; //合约价值,如1张代表0.01个币
if (exchange_info[pair].CtValCcy != symbol){ //价值的计价货币,这里不处理币本位的情况,如1张价值100美元
throw "[trans]不支持币本位|Don't support coin margin type[/trans]"
}
}
}
}
多種多様な戦略を設計するには,全市場へのアクセスが必要である.このような集積市場インターフェースは不可欠であり,GetTickers機能はほとんどの主流取引所をサポートする.
function UpdateTicker() {
//更新价格
let ticker = exchange.GetTickers();
if (!ticker) {
Log("[trans]获取行情失败|Fail to get market[/trans]", GetLastError());
return;
}
Info.time.update_ticker_time = Date.now();
for (let i = 0; i < ticker.length; i++) {
let symbol = ticker[i].Symbol.split('_')[0];
if (!ticker[i].Symbol.split('.')[0].endsWith(Info.base_coin) || Info.trade_symbols.indexOf(symbol) < 0 || !ticker[i].Symbol.endsWith('swap')) {
continue;
}
Info.ticker[symbol].ask = parseFloat(ticker[i].Sell);
Info.ticker[symbol].bid = parseFloat(ticker[i].Buy);
Info.ticker[symbol].last = parseFloat(ticker[i].Last);
}
}
フューチャー口座情報は,エクイティ総利得フィールドとUPnLフィールドが追加され,追加の処理による不適合が不要である.GetPositions関数は,すべてのポジションを取得することをサポートする.詳細は,ポジションの数を1つの契約価値に掛けることで実際の数を得ることである. OKX永続のように,ウェブページでは数値による取引を選択できるが,APIは張数によるものである.
function UpdateAccount() {
//更新账户
if (Date.now() - Info.time.update_account_time < 60 * 1000) {
return;
}
Info.time.update_account_time = Date.now();
let account = exchange.GetAccount();
if (account === null) {
Log("[trans]更新账户失败|Fail to get account[/trans]");
return;
}
Info.account.margin_used = _N(account.Equity - account.Balance, 2);
Info.account.margin_balance = _N(account.Equity, 2); //当前余额
Info.account.margin_free = _N(account.Balance, 2);
Info.account.wallet_balance = _N(account.Equity - account.UPnL, 2);
Info.account.unrealised_profit = _N(account.UPnL, 2);
if (!Info.account.init_balance) {
if (_G("init_balance") && _G("init_balance") > 0) {
Info.account.init_balance = _N(_G("init_balance"), 2);
} else {
Info.account.init_balance = Info.account.margin_balance;
_G("init_balance", Info.account.init_balance);
}
}
Info.account.profit = _N(Info.account.margin_balance - Info.account.init_balance, 2);
Info.account.profit_rate = _N((100 * Info.account.profit) / init_balance, 2);
}
function UpdatePosition() {
let pos = exchange.GetPositions(Info.base_coin + ".swap");
if (!pos) {
Log("[trans]更新仓位超时|Fail to get position[/trans]");
return;
}
Info.time.update_pos_time = Date.now();
let position_info = {};
for (let symbol of Info.trade_symbols) {
position_info[symbol] = {
amount: 0,
hold_price: 0,
unrealised_profit: 0
}; //有的交易所没有仓位返回空
}
for (let k = 0; k < pos.length; k++) {
let symbol = pos[k].Symbol.split("_")[0];
if (!pos[k].Symbol.split(".")[0].endsWith(Info.base_coin) || Info.trade_symbols.indexOf(symbol) < 0) {
continue;
}
if (position_info[symbol].amount != 0){
throw "[trans]需要单向持仓|Position need net Mode:[/trans]";
}
position_info[symbol] = {
amount: pos[k].Type == 0 ? pos[k].Amount * Info.precision[symbol].ctVal : -pos[k].Amount * Info.precision[symbol].ctVal,
hold_price: pos[k].Price,
unrealised_profit: pos[k].Profit
};
}
Info.count = { long: 0, short: 0, total: 0, leverage: 0 };
for (let symbol in position_info) {
let deal_volume = Math.abs(position_info[symbol].amount - Info.position[symbol].amount);
let direction = position_info[symbol].amount - Info.position[symbol].amount > 0 ? 1 : -1;
if (deal_volume) {
let deal_price = direction == 1 ? Info.order[symbol].buy.price : Info.order[symbol].sell.price;
Log(
symbol,
"[trans]仓位更新:|Position update:[/trans]",
_N(Info.position[symbol].value, 1),
" -> ",
_N(position_info[symbol].amount * Info.ticker[symbol].last, 1),
direction == 1 ? "[trans], 买|. Buy[/trans]" : "[trans], 卖|, Sell[/trans]",
"[trans], 成交价:| Deal price: [/trans]",
deal_price,
"[trans], 成本价:| Hold price: [/trans]",
_N(Info.position[symbol].hold_price, Info.precision[symbol].price_precision),
);
}
Info.position[symbol].amount = position_info[symbol].amount;
Info.position[symbol].hold_price = position_info[symbol].hold_price;
Info.position[symbol].value = _N(Info.position[symbol].amount * Info.ticker[symbol].last, 2);
Info.position[symbol].unrealised_profit = position_info[symbol].unrealised_profit;
Info.count.long += Info.position[symbol].amount > 0 ? Math.abs(Info.position[symbol].value) : 0;
Info.count.short += Info.position[symbol].amount < 0 ? Math.abs(Info.position[symbol].value) : 0;
}
Info.count.total = _N(Info.count.long + Info.count.short, 2);
Info.count.leverage = _N(Info.count.total / Info.account.margin_balance, 2);
}
取引は,ここでも表紙の数の問題に対処し,最新のCreateOrder関数を使って注文を処理することが便利です.
function Order(symbol, direction, price, amount, msg) {
let ret = null;
let pair = symbol + "_" + Info.base_coin + ".swap"
ret = exchange.CreateOrder(pair, direction, price, amount, msg)
if (ret) {
Info.order[symbol][direction].id = ret;
Info.order[symbol][direction].price = price;
}else {
Log(symbol, direction, price, amount, "[trans]下单异常|Error on order[/trans]");
}
}
function Trade(symbol, direction, price, amount, msg) {
price = _N(price - (price % Info.precision[symbol].tick_size), Info.precision[symbol].price_precision);
amount = amount / Info.precision[symbol].ctVal;
amount = _N(amount - (amount % Info.precision[symbol].amount_size), Info.precision[symbol].amount_precision);
amount = Info.precision[symbol].max_qty > 0 ? Math.min(amount, Info.precision[symbol].max_qty) : amount;
let new_order = false;
if (price > 0 && Math.abs(price - Info.order[symbol][direction].price) / price > 0.0001) { //两次订单有差价了才撤单
new_order = true;
}
if (amount <= 0 || Info.order[symbol][direction].id == 0) { //传入amount为0 撤单
new_order = true;
}
if (new_order) {
if (Info.order[symbol][direction].id) { //原有订单撤销
CancelOrder(symbol, direction, Info.order[symbol][direction].id);
Info.order[symbol][direction].id = 0;
}
if ( //延时过高不下单
Date.now() - Info.time.update_pos_time > 2 * Info.interval * 1000 ||
Date.now() - Info.time.update_ticker_time > 2 * Info.interval * 1000 ||
) {
return;
}
if (price * amount <= Info.precision[symbol].min_notional || amount < Info.precision[symbol].min_qty) {
Log(symbol, "[trans]下单量太低|amount is too small[/trans]", price * amount);
return;
}
Order(symbol, direction, price, amount, msg);
}
}
一般的に,2つの表,口座情報と取引情報が表示される.
unction UpdateStatus() {
if (Date.now() - Info.time.update_status_time < 4000) {
return;
}
Info.time.update_status_time = Date.now();
let table1 = {
type: "table",
title: "[trans]账户信息|Account info[/trans]",
cols: [
"[trans]初始余额|Initial Balance[/trans]",
"[trans]钱包余额|Wallet balance[/trans]",
"[trans]保证金余额|Margin balance[/trans]",
"[trans]已用保证金|Used margin[/trans]",
"[trans]可用保证金|Avaiable margin[/trans]",
"[trans]总收益|Profit[/trans]",
"[trans]收益率|Profit rate[/trans]",
"[trans]未实现收益|Unrealised profit[/trans]",
"[trans]总持仓|Total value[/trans]",
"[trans]已用杠杆|Leverage-used[/trans]",
"[trans]循环延时|Delay[/trans]",
],
rows: [
[
Info.account.init_balance,
Info.account.wallet_balance,
Info.account.margin_balance,
Info.account.margin_used,
Info.account.margin_free,
Info.account.profit,
Info.account.profit_rate + "%",
_N(Info.account.unrealised_profit, 2),
_N(Info.count.total, 2),
Info.count.leverage,
Info.time.loop_delay + "ms",
],
],
};
let table2 = {
type: "table",
title: "[trans]交易对信息|Symbol info[/trans]",
cols: [
"[trans]币种|Symbol[/trans]",
"[trans]方向|Direction[/trans]",
"[trans]数量|Amount[/trans]",
"[trans]持仓价格|Hold price[/trans]",
"[trans]持仓价值|Value[/trans]",
"[trans]现价|Price[/trans]",
"[trans]挂单买价|Buy price[/trans]",
"[trans]挂单卖价|Sell price[/trans]",
"[trans]未实现盈亏|Unrealised profit[/trans]",
],
rows: [],
};
for (let i in Info.trade_symbols) {
let symbol = Info.trade_symbols[i];
table2.rows.push([
symbol,
Info.position[symbol].amount > 0 ? "LONG" : "SHORT",
_N(Info.position[symbol].amount, Info.precision[symbol].amount_precision+2),
_N(Info.position[symbol].hold_price, Info.precision[symbol].price_precision),
_N(Info.position[symbol].value, 2),
_N(Info.ticker[symbol].last, Info.precision[symbol].price_precision),
Info.order[symbol].buy.price,
Info.order[symbol].sell.price,
_N(Info.position[symbol].unrealised_profit, 2),
]);
}
LogStatus(
"[trans]初始化时间: | Initial date: [/trans]" + _D(new Date(Info.time.start_time)) + "\n",
"`" + JSON.stringify(table1) + "`" + "\n" + "`" + JSON.stringify(table2) + "`\n",
"[trans]最后执行时间: |Last run date: [/trans]" + _D() + "\n",
);
if (Date.now() - Info.time.update_profit_time > 5 * 60 * 1000) {
UpdateAccount();
LogProfit(_N(Info.account.profit, 3));
Info.time.update_profit_time = Date.now();
}
}
脚架が組み立てられたら,最も基本的な取引論理コードは簡単です.
function MakeOrder() {
for (let i in Info.trade_symbols) {
let symbol = Info.trade_symbols[i];
let buy_price = Info.ticker[symbol].bid;
let buy_amount = 50 / buy_price;
if (Info.position[symbol].value < 2000){
Trade(symbol, "buy", buy_price, buy_amount, symbol);
}
}
}
function OnTick() {
try {
UpdateTicker();
UpdatePosition();
MakeOrder();
UpdateStatus();
} catch (error) {
Log("[trans]循环出错: | Loop error: [/trans]" + error);
}
}
function main() {
InitInfo();
while (true) {
let loop_start_time = Date.now();
if (Date.now() - Info.time.last_loop_time > Info.interval * 1000) {
OnTick();
Info.time.last_loop_time = Date.now();
Info.time.loop_delay = Date.now() - loop_start_time;
}
Sleep(5);
}
}
この記事では,最新のAPIを活用したシンプルで持続的な契約多通貨取引フレームワークを提示し,より簡単に,迅速に互換性戦略を構築し,試してみる価値があります.
張小宝郎/upload/asset/2ff0c8463217e861599ae.png この質問は,私が管理者レベルが低いからですか? それとも何か問題ですか?
イアンゼン123ありがとう,ここがどんなに深い扉なのか理解しました.
小草SYMBOLSは,ポリシーで定義されたグローバル変数で,ポリシーパラメータで定義する必要があります.