Учебное пособие по разработке стратегии для платформы FMZ Quant

Автор:Нинабадасс., Создано: 2022-03-22 09:00:57, Обновлено: 2022-03-29 10:02:52

[TOC] Прежде чем выучить этот учебник, вы должны изучитьНачните с квантовой платформы FMZиЭлементарный учебник для платформы FMZ Quant Strategy Writing, и овладеть языками программирования.Элементарное руководство охватывает наиболее часто используемые функции, но есть много функций и функций, которые не были представлены, и они не будут охвачены в этом руководстве.После изучения этого учебника, вы сможете писать более бесплатные и индивидуальные стратегии, а платформа FMZ Quant - это просто инструмент.

Доступ к сырым данным платформы

Платформа FMZ Quant включает в себя все поддерживаемые платформы. Чтобы сохранить единообразие, наша поддержка API одной платформы по-прежнему не завершена. Например, GetRecords может передавать количество K-линий или время начала, в то время как это фиксируется на платформе FMZ; некоторые платформы поддерживают партийный заказ, в то время как FMZ не поддерживает этого и т. Д. Поэтому есть необходимость в способе прямого доступа к данным платформы.Для публичных интерфейсов (например, котировок на рынке) вы можете использоватьHttpQuery, а для зашифрованных интерфейсов (включающих информацию об аккаунте) необходимо использоватьIO.Для конкретных входящих параметров, пожалуйста, обратитесь к соответствующему документу API платформы.Infoполе возвращает необработанную информацию, но это все равно не меняет проблему не поддерживающих интерфейсы.

GetRawJSON ((()

Он возвращает сырой контент (строки), запрошенный последним REST API, который может быть использован для анализа расширенной информации самостоятельно.

function main(){
    var account = exchange.GetAccount() //the account doesn't contain all data returned by the request
    var raw = JSON.parse(exchange.GetRawJSON())//raw data returned by GetAccount()
    Log(raw)
}

HttpQuery() Доступ к публичным интерфейсам

Чтобы получить доступ к общедоступным интерфейсам, Js может использоватьHttpQuery, и Python может использовать связанные пакеты, такие какurllibилиrequests.

HttpQuery по умолчанию использует метод GET и поддерживает больше функций; для получения дополнительной информации ознакомьтесь с документом API.

var exchangeInfo = JSON.parse(HttpQuery('https://api.binance.com/api/v1/exchangeInfo'))
Log(exchangeInfo)
var ticker = JSON.parse(HttpQuery('https://api.binance.com/api/v1/ticker/24hr'))
var kline = JSON.parse(HttpQuery("https://www.quantinfo.com/API/m/chart/history?symbol=BTC_USD_BITFINEX&resolution=60&from=1525622626&to=1561607596"))

Пример Python с использованием запросов:

import requests
resp = requests.get('https://www.quantinfo.com/API/m/chart/history?symbol=BTC_USD_BITFINEX&resolution=60&from=1525622626&to=1561607596')
data = resp.json()

IO доступа к зашифрованным интерфейсам

Для интерфейсов, требующих подписи API-KEY, может использоваться функция IO, и пользователям нужно заботиться только о входящих параметрах, и конкретный процесс подписи будет завершен базовым слоем.

Платформа FMZ в настоящее время не поддерживает BitMEX стоп-лосс ордера, которые могут быть реализованы через IO, согласно следующим шагам:

  • Сначала найдите страницу инструкций BitMEX API:https://www.bitmex.com/api/explorer/;
  • Тогда найдите адрес заказа BitMEX по адресу:https://www.bitmex.com/api/v1/order, методомPOST; для FMZ уже внутренне указано базовый адрес, вам нужно только передать в /api/v1/order.
  • соответствующие параметры:symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop.

Специфический код:

var id = exchange.IO("api", "POST", "/api/v1/order", "symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop")
// You can also pass in the object 
var id = exchange.IO("api", "POST", "/api/v1/order", "", JSON.stringify({symbol:"XBTUSD",side:"Buy",orderQty:1,stopPx:4000,ordType:"Stop"}))

Больше примеров IO:https://www.fmz.com/bbs-topic/3683

Использование websocket

В основном, все криптовалютные платформы поддерживают websocket для отправки рыночных котировок, а некоторые платформы поддерживают websocket для обновления информации об аккаунте. По сравнению с API отдыха, websocket обычно имеет преимущества, такие как низкая задержка, высокая частота и не ограничивается частотой API отдыха платформы и т. Д. Недостатком является то, что есть проблема прерывания, обработка которой не интуитивно понятна.

В этой статье в основном рассказывается о том, как использовать язык JavaScript и как использовать функцию Dial, включенную платформой для подключения, на платформе FMZ Quant; для получения конкретных инструкций и параметров вы можете искать в документе Dial; для реализации различных функций функция Dial была обновлена несколько раз. В этой статье будут рассмотрены и представлены стратегии, основанные на событиях, а также вопрос подключения нескольких платформ. Python также может использовать функцию Dial или соответствующую библиотеку.

1.Соединение Websocket

Как правило, напрямую подключаться через Websocket; например, чтобы получить Binance Tricker push:

var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")

Если возвращенные данные находятся в сжатом формате, при подключении следует указать спецификацию; compress относится к сжатому формату, а mode представляет, какие возвращенные данные необходимо сжимать; например, при подключении к OKEX:

var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")

Функция Dial поддерживает повторное подключение, которое выполняется базовым Golang. Если обнаруженное соединение нарушится, оно будет воссоединено. Для запросов данных, уже находящихся в URL-адресе, таких как пример Binance, это очень удобно и рекомендуется. Для тех, кому нужно отправлять подписку сообщения, они могут поддерживать механизм повторного подключения сами.

var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")

Для подписки на wss сообщения, некоторые запросы платформы находятся в URL, а некоторые должны отправлять подписанные каналы сами, такие как coinbase:

client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')

Зашифрованное соединение интерфейса

Как правило, websocket используется для чтения котировок на рынке, но также может использоваться для получения заказов и продвижения счетов. Движение таких зашифрованных данных иногда имеет большую задержку и должно использоваться с осторожностью. Поскольку метод шифрования более сложен, вот несколько примеров для справки. Обратите внимание, что требуется только AccessKey, который может быть установлен в качестве параметра стратегии. Если SecretKey требуется, он может быть подразумето вызван функцией exchange.HMAC(() для обеспечения безопасности.

    //Push example of Huobi Futures
    var ACCESSKEYID = 'accesskey of your Huobi account'
    var apiClient = Dial('wss://api.hbdm.com/notification|compress=gzip&mode=recv')
    var date = new Date(); 
    var now_utc =  Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
    var utc_date = new Date(now_utc)
    var Timestamp = utc_date.toISOString().substring(0,19)
    var quest = 'GET\napi.hbdm.com\n/notification\n'+'AccessKeyId='+ACCESSKEYID+'&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=' + encodeURIComponent(Timestamp)
    var signature = exchange.HMAC("sha256", "base64", quest, "{{secretkey} }") // Remove the extra blank spaces between }}
    auth = {op: "auth",type: "api",AccessKeyId: ACCESSKEYID, SignatureMethod: "HmacSHA256",SignatureVersion: "2", Timestamp: Timestamp, Signature:encodeURI(signature)}
    apiClient.write(JSON.stringify(auth))
    apiClient.write('{"op": "sub","cid": "orders","topic": "orders.btc'}')
    while (true){
        var data = datastream.read()
        if('op' in data && data.op == 'ping'){
            apiClient.write(JSON.stringify({op:'pong', ts:data.ts}))
        }
    }
    
    // Push example of Binance; pay attention that listenKey needs to be updated regularly   
    var APIKEY = 'accesskey of your Binance account'
    var req = HttpQuery('https://api.binance.com/api/v3/userDataStream',{method: 'POST',data: ''},null,'X-MBX-APIKEY:'+APIKEY);
    var listenKey = JSON.parse(req).listenKey;
    HttpQuery('https://api.binance.com/api/v3/userDataStream', {method:'DELETE',data:'listenKey='+listenKey}, null,'X-MBX-APIKEY:'+APIKEY);
    listenKey = JSON.parse(HttpQuery('https://api.binance.com/api/v3/userDataStream','',null,'X-MBX-APIKEY:'+APIKEY)).listenKey;
    var datastream = Dial("wss://stream.binance.com:9443/ws/"+listenKey+'|reconnect=true',60);
    var update_listenKey_time =  Date.now()/1000;
    while (true){
        if (Date.now()/1000 - update_listenKey_time > 1800){
            update_listenKey_time = Date.now()/1000;
            HttpQuery('https://api.binance.com/api/v3/userDataStream', {method:'PUT',data:'listenKey='+listenKey}, null,'X-MBX-APIKEY:'+APIKEY);
        }
        var data = datastream.read()
    }

    // push example of BitMEX
    var APIKEY = "your Bitmex API ID"
    var expires = parseInt(Date.now() / 1000) + 10
    var signature = exchange.HMAC("sha256", "hex", "GET/realtime" + expires, "{{secretkey} }")// secretkey is automatically replaced during execution, so no need to fill in
    var client = Dial("wss://www.bitmex.com/realtime", 60)
    var auth = JSON.stringify({args: [APIKEY, expires, signature], op: "authKeyExpires"})
    var pos = 0
    client.write(auth)
    client.write('{"op": "subscribe", "args": "position"}')
    while (true) {
        bitmexData = client.read()
        if(bitmexData.table == 'position' && pos != parseInt(bitmexData.data[0].currentQty)){
            Log('position change', pos, parseInt(bitmexData.data[0].currentQty), '@')
            pos = parseInt(bitmexData.data[0].currentQty)
        }
    }

3.Читать веб-сокет

Как правило, его можно читать непрерывно в бесконечной петле.

function main() {
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
    while (true) {
        var msg = client.read()
        var data = JSON.parse(msg) // Parse json strings into quotable objects 
// Process data 
    }
}

Скорость продвижения данных wss очень быстрая. Подслой Golang будет кэшировать все данные в очереди, и когда вызовы программы будут прочитаны, данные будут возвращены в свою очередь. Однако такие операции, как размещение заказа на боте, вызовут задержки, что может привести к накоплению данных. Для информации, такой как продвижение исполнения торговли, продвижение счета и продвижение глубины интерполяции, нам нужны данные истории. Для данных рынка котировок в большинстве случаев мы заботимся только о последних данных, а не о данных истории.

Еслиread()добавляет никаких параметров, он вернет самые старые данные, и блокировать до возвращения, когда нет данных.client.read(-2)чтобы немедленно вернуть последние данные, но когда нет данных, он вернет нуль, который должен быть оценен до ссылки.

В зависимости от того, как обращаться со старыми кэшированными данными и блокируется ли она, когда нет данных, read имеет разные параметры, как показано в таблице ниже, что выглядит сложно, но делает программу более гибкой.img

4.Соединение с несколькими платформами с помощью Websocket

В этом случае очевидно, что просто использование read() не работает в программе, потому что одна платформа заблокирует ожидающие сообщения, а другая платформа не будет принимать даже если есть новые сообщения.

    function main() {
        var binance = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
        var coinbase = Dial("wss://ws-feed.pro.coinbase.com", 60)
        coinbase.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
        while (true) {
            var msgBinance = binance.read(-1) // Parameter -1 represents no data and return null immediately; it will not occur that being blocked before there is data to be returned 
            var msgCoinbase = coinbase.read(-1)
            if(msgBinance){
                // at this time, Binance has data to return 
            }
            if(msgCoinbase){
                // at this time, coinbase has data to return 
            }
            Sleep(1) // Sleep for 1 millisecond
        }
    }

5.Проблемы с отключением и воссоединением

Эта часть обработки является более проблематичной, поскольку данные могут быть прерваны или задержка продлится чрезвычайно долго. Даже если можно получить сердцебиение, это не означает, что данные все еще подталкиваются. Вы можете установить интервал событий; если после интервала не будет получено обновления, перезагрузите; лучше сравнить результаты, возвращенные rest через определенный период времени, чтобы увидеть, точны ли данные.

6.Использование общих программных рамок Websocket

Для push-данных, которые были использованы, программа естественно будет записываться как вызванная событием; обратите внимание на частоту push-данных, потому что высокочастотные запросы приведут к блокировке; как правило, вы можете написать:

    var tradeTime = Date.now()
    var accountTime = Date.now()
    function trade(data){
        if(Date.now() - tradeTime > 2000){//Here it limits only one trade in 2 seconds 
            tradeTime = Date.now()
            // Trading logic
        }
    }
    function GetAccount(){
        if(Date.now() - accountTime > 5000){//Here it limits GetAccount only once in 5 seconds 
            accountTime = Date.now()
            return exchange.GetAccount()
        }
    }
    function main() {
        var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true");
        while (true) {
            var msg = client.read()
            var data = JSON.parse(msg)
            var account = GetAccount()
            trade(data)
        }
    }

7.Conclusion

Способ подключения, способ передачи данных, подписанный контент и формат данных веб-сокета на каждой платформе часто различаются, поэтому платформа не инкапсулирует его и должна использовать функцию Dial для подключения самостоятельно.

PS: Несмотря на то, что некоторые платформы не предоставляют цитаты websocket, на самом деле, когда вы входите на сайт, чтобы использовать функцию отладки, вы обнаружите, что все они используют push websocket.

Конкуренция многопотоков

JavaScript может реализовать совместимость с функцией Go, а Python может использовать соответствующую многопоточную библиотеку.

Во время реализации количественных стратегий одновременное исполнение может уменьшить задержку времени и повысить эффективность. Возьмем, к примеру, бота стратегии хеджирования.

var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()

Когда запрос на API отдыха задерживается, например, время задержки составляет 100 миллисекунд, тогда время для получения глубины дважды на самом деле отличается; если требуется больше доступа, проблемы с задержкой будут более очевидными, что повлияет на исполнение стратегии.

Поскольку JavaScript не имеет мультипотока, то для решения этой проблемы подкладка инкапсулирует функцию Go. Функцию Go можно использовать для API, требующих доступа к сети, таких какGetDepth, GetAccountи так далее.IOтакже поддерживается, например:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders)), но из-за механизма проектирования его реализация более утомительна.

var a = exchanges[0].Go("GetDepth")
var b = exchanges[1].Go("GetDepth")
var depthA = a.wait() // Call "wait" method to wait for the return of the asynchronous GetDepth result 
var depthB = b.wait()

В большинстве простых случаев написание стратегий таким образом вполне нормально. Но обратите внимание, что процесс повторяется каждый раз, когда цикл стратегии, и промежуточные переменные a и b на самом деле только временно вспомогательные. Если у нас много одновременных задач, мы должны дополнительно записывать соответствие между а и глубинойA, b и глубинойB. Когда наши одновременные задачи неопределенны, ситуация более сложна. Поэтому мы надеемся реализовать функцию: при написании функции Go одновременно, связать переменную в то же время; когда результаты одновременного выполнения возвращается, значение результата автоматически присваивается переменной, таким образом, устраняя необходимость промежуточных переменных и сделать программу более лаконичной. Конкретная реализация выглядит следующим образом:

function G(t, ctx, f) {
    return {run:function(){
        f(t.wait(1000), ctx)
    }}
}

Мы определили функцию G, где параметр t - это функция Go, которую нужно выполнить, ctx - это функция записи контекста программы, и f - это функция, которая присваивает определенное значение.

В это время общая программа может быть написана как модель, похожая на модель "производитель-потребитель" (с некоторыми отличиями), производитель непрерывно отправляет задачи, а потребитель выполняет их одновременно.

var Info = [{depth:null, account:null}, {depth:null, account:null}] // If we need to obtain the depth and account of the two platforms, more information can also be put in, such as order ID and status, etc.
var tasks = [ ] // Global task list 

function produce(){ // Issue all kinds of concurrent tasks
  // Here the task producing logic has been omitted, only for demo
  tasks.push({exchange:0, ret:'depth', param:['GetDepth']})
  tasks.push({exchange:1, ret:'depth', param:['GetDepth']})
  tasks.push({exchange:0, ret:'sellID', param:['Buy', Info[0].depth.Asks[0].Price, 10]})
  tasks.push({exchange:1, ret:'buyID', param:['Sell', Info[1].depth.Bids[0].Price, 10]})
}
function worker(){
    var jobs = []
    for(var i=0;i<tasks.length;i++){
        var task = tasks[i]
        jobs.push(G(exchanges[task.exchange].Go.apply(this, task.param), task, function(v, task) {
                    Info[task.exchange][task.ret] = v // Here "v" is the return value of the concurrent Go function "wait()", and you can think about it 
                }))
    }
    _.each(jobs, function(t){
            t.run() // Here all tasks are executed concurrently 
        })
    tasks = []
}
function main() {
    while(true){
        produce()         // Give trading command
        worker()        // Concurrently execute
        Sleep(1000)
    }
}

По-видимому, только простая функция была реализована в вышеуказанных операциях. На самом деле, это значительно упростило сложность кода. Нам нужно только заботиться о том, какие задачи должна генерировать программа, и программа worker() автоматически выполняет их одновременно и возвращает соответствующие результаты. Гибкость значительно улучшилась.

Рисование по функции диаграммы

В начальном учебном пособии рекомендуется использовать библиотеку классов рисования при введении рисования, которая может удовлетворить потребности в большинстве случаев. Если вам нужна дальнейшая настройка, вы можете напрямую управлять объектом Chart.

Внутренние параметрыChart({…})являются объектами HighStock и HighCharts, но дополнительным параметром__isStockFMZ в основном поддерживает базовые модули HighCharts и HighStock, но не поддерживает дополнительные модули.

Конкретный пример HighCharts:https://www.highcharts.com/demo; Пример HighStock:https://www.highcharts.com/stock/demoВы можете обратиться к кодам в этих примерах, и пересадить их в FMZ удобно.

Вы можете вызвать add ([series index ((like 0), data]) для добавления данных в серию с указанным индексом. Call reset (()) для очистки данных диаграммы; reset может взять параметр числа и указать сумму, которую нужно сохранить. Поддерживается множественное отображение диаграммы, которое необходимо только передать в параметры массива во время конфигурации, например: var chart = Chart (([{...}, {...}, {...}). Например, если в диаграмме 1 есть две серии, в диаграмме 2 есть одна серия, а в диаграмме 3 есть одна серия, при вызове add, ID серии 0 и 1 указаны, чтобы отдельно представлять данные двух серий в обновленном диаграмме 1; ID серии 2 указана для представления данных первой серии в диаграмме 2; ID серии 3 указана для представления первой серии данных в диаграмме 3.

Конкретный пример:

var chart = { // This "chart" in JS is an object; before using the Chart function, we need to declare the object variable of a configured chart "chart" 
    __isStock: true,                                    // Mark whether it is a general chart; you can change it to false and try to operate it, if you are interested 
    tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},    // Zoom tool
    title : { text : 'spread chart'},                       // Theme
    rangeSelector: {                                    // Choose the range
        buttons:  [{type: 'hour',count: 1, text: '1h'}, {type: 'hour',count: 3, text: '3h'}, {type: 'hour', count: 8, text: '8h'}, {type: 'all',text: 'All'}],
        selected: 0,
        inputEnabled: false
    },
    xAxis: { type: 'datetime'},                         // Horizontal axis, namely X axis; currently set type: time
    yAxis : {                                           // Vertical axis, namely Y axis; the default changes according to the data 
        title: {text: 'spread'},                           // Theme
        opposite: false,                                // whether to enable the vertical axis on the right 
    },
    series : [                                          // Data series; the attribute saves all kinds of data series (lines, K-lines, labels, etc.)
        {name : "line1", id : "line1,buy1Price", data : []},  // The index is 0; the data stroed in the data array is the data of the index series 
        {name : "line2", id : "line2,lastPrice", dashStyle : 'shortdash', data : []}, // The index is 1; set  dashStyle: 'shortdash', namely: set  dashed line
    ]
};
function main(){
    var ObjChart = Chart(chart);  // Call the Chart function, and initialize the chart 
    ObjChart.reset();             // Empty 
    while(true){
        var nowTime = new Date().getTime();   // Obtain the timestamp of this polling, namely a millisecond tiemstamp, to ensure the location of writing to the X axis in the chart 
        var ticker = _C(exchange.GetTicker);  // Obtain the market quotes data
        var buy1Price = ticker.Buy;           // Get buy one price from the return value of the market quotes 
        var lastPrice = ticker.Last + 1;      // Get the final executed price, and we add 1 to split the 2 lines 
        ObjChart.add([0, [nowTime, buy1Price]]); // Use the timestamp as the value of X, and buy one price as the value of Y; pass in the data series of index 0  
        ObjChart.add([1, [nowTime, lastPrice]]); // Same as above. 
        Sleep(2000);
    }
}

Пример использования макета диаграммы:https://www.fmz.com/strategy/136056

Расширенный обратный тест

Локальный тест Python

Конкретный адрес открытого кода:https://github.com/fmzquant/backtest_python

Установка

Введите следующую команду в командную строку:

pip install https://github.com/fmzquant/backtest_python/archive/master.zip

Простой пример

Установите параметры обратного теста в начале кода стратегии в виде замечания, и подробности будут показаны на кнопке "Сохранить настройки" на странице "Редактировать стратегию" на веб-сайте FMZ.

'''backtest
start: 2018-02-19 00:00:00
end: 2018-03-22 12:00:00
period: 15m
exchanges: [{"eid":"OKEX","currency":"LTC_BTC","balance":3,"stocks":0}]
'''
from fmz import *
task = VCtx(__doc__) # initialize backtest engine from __doc__
print exchange.GetAccount()
print exchange.GetTicker()
print task.Join() # print backtest result

Обратный тест

Для полных стратегий необходимы бесконечные петли, ошибка EOF будет поднята после завершения обратного теста; следовательно, мы должны хорошо справляться с допущенными ошибками.

# !/usr/local/bin/python
# -*- coding: UTF-8 -*-

'''backtest
start: 2018-02-19 00:00:00
end: 2018-03-22 12:00:00
period: 15m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD","balance":10000,"stocks":3}]
'''

from fmz import *
import math
import talib

task = VCtx(__doc__) # initialize backtest engine from __doc__

# ------------------------------ Start of the Strategy  --------------------------

print exchange.GetAccount()     # Call some interfaces, and print their return values 
print exchange.GetTicker()

def adjustFloat(v):             # the custom functions in the strategy 
    v = math.floor(v * 1000)
    return v / 1000

def onTick():
    Log("onTick")
    # Specific strategy code 


def main():
    InitAccount = GetAccount()
    while True:
        onTick()
        Sleep(1000)

# ------------------------------ End of the Strategy --------------------------

try:
    main()                     # The end of the backtest will raise EOFError() to stop stop the backtest loop. Therefore, we should handle with the error, and call task.Join() to print the backtest result, after the error is detected  
except:
    print task.Join()         

Данные обратного теста по умолчанию

exchange.SetData(arr) переключает источник данных backtest и использует пользовательские данные K-линии. Параметр arr представляет собой массив, элементы которого являются данными K-линейных полос (т.е. K-линейный массив данных, который временно поддерживает только JavaScript backtest).

в массиве arr - формат данных одного элемента:

[
    1530460800,    // time     Timestamp 
    2841.5795,     // open     Open Price 
    2845.6801,     // high     Highest Price
    2756.815,      // low      Lowest Price
    2775.557,      // close    Close Price
    137035034      // volume   Executed Volume 
]

Источник данных можно импортировать в Стандарт.

function init() {                                                          // The init function in the template will be executed first when the template is loaded; ensure the exchange.SetData(arr) function is executed first, initialized, and set the data to the backtest system
    var arr = [                                                            // The K-line data to be used during backtest
        [1530460800,2841.5795,2845.6801,2756.815,2775.557,137035034],      // The data of the earliest K-line bar
        ... ,                                                              // If the K-line data is too long, use "..." to represent the omitted data here
        [1542556800,2681.8988,2703.5116,2674.1781,2703.5116,231662827]     // The data of the latest K-line bar 
    ]
    exchange.SetData(arr)                                                  // Import the custom data mentioned above 
    Log("Import data successfully")
}

Примечание: убедитесь, что вы импортируете пользовательские данные сначала (т. е. вызовите функцию exchange.SetData для настройки данных) во время инициализации. Период пользовательских данных K-линии должен соответствовать периоду K-линии нижнего слоя, установленному на странице бэкстеста, то есть: пользовательские данные K-линии; время K-линии составляет 1 минуту, поэтому период K-линии нижнего слоя, установленный в бэкстесте, также должен быть установлен на 1 минуту.

Использование платформ, не поддерживаемых FMZ

Если API неподдерживаемой платформы точно такой же, как и поддерживаемой платформы, за исключением базового адреса, неподдерживаемую платформу можно поддерживать, переключая базовый адрес.

exchange.IO("base", "http://api.huobi.pro") 
//http://api.huobi.pro is the base address of the unsupported platform API, and notice not to add api/v3 and so on, for the address will be automatically completed

Не все платформы поддерживаются FMZ, но наша платформа предоставила метод доступа общего протокола.

  • Когда вы пишете код для доступа к платформе, программа создает веб-сервис.
  • При добавлении платформы в FMZ укажите адрес и порт веб-сервиса.
  • Когда докер запускает платформенный бот общего протокола, запрос на доступ к API в стратегии будет отправлен в общий протокол.
  • Общий протокол будет получать доступ к платформе по запросу и возвращает результат докеру.

Проще говоря, общий протокол похож на посредника, прокси-запрос докера и возвращение данных, в соответствии с соответствующим стандартом. Код общего протокола необходимо заполнить самостоятельно. Написание общего протокола на самом деле означает, что вы можете получить доступ к платформе исключительно и завершить стратегию. FMZ официальный иногда выпускает exe версию общего протокола платформ. Общий протокол также может быть выполнен в Python, который затем может быть запущен на докере как обычный бот.

Специфическое введение общего протокола:https://www.fmz.com/bbs-topic/9120Пример написания протокола в Python:https://www.fmz.com/strategy/101399

Создание собственной количественной платформы

Так же, как различные операции платформы могут быть реализованы через API, веб-сайт FMZ также основан на API. Вы можете подать заявку на свой собственный API-Ключ веб-сайта FMZ для реализации функций, таких как create, restart, DeleteRobot, GetRobotList GetRobotLogs и т. Д. Пожалуйста, ознакомьтесь с разделом API Extension of FMZ Platform в документе API.

Благодаря мощной расширяемости платформы FMZ Quant, вы можете создать свою собственную количественную платформу на основе расширения API, позволяя вашим пользователям запускать ботов на вашей платформе и т. Д. Конкретная ссылка:https://www.fmz.com/bbs-topic/1697 .

Стать партнером FMZ

Продвижение NetEase Classroom

Рынок торговли криптовалютами привлекает все больше и больше внимания количественных трейдеров из-за своей специфики. На самом деле, программа торговли стала основным направлением криптовалюты, и такие стратегии, как хеджирование и рыночное формирование, всегда активны на рынке. Начинающие с слабыми программами хотят войти в эту новую область, сталкиваясь с многочисленными платформами и меняющимися API, полными трудностей.www.fmz.comДля того чтобы пройти курс на NetEase, вам нужно всего 20 юаней, и курс полностью предназначен для новичков.

ПродвижениеКурс количественной торговли криптовалютами на NetEase Cloud Classroom. Входите в NetEase Cloud Classroom и поделитесь ссылкой на курс (ссылка имеет уникальный курс ID). Другие, которые регистрируются и покупают курс через эту ссылку, принесут вам 50% от общего количества в качестве комиссии, а именно 10 юаней. Следуйте за публичной учетной записью WeChat NetEase Cloud Classroom Premium Course Promotion, чтобы снять наличные деньги. Вы также можете пригласить других продвигать курс в группе Weibo или QQ.

Присоединение

Потребители, которые нажимают на ссылку на продвижение, регистрируются и пополняют в течение полугода, будут пользоваться политикой, согласно которой наша компания выплатит скидку в соответствии с действительной суммой в действительном порядке. Комиссия будет возвращена на счет промоутера в виде очков. Пользователи могут обменять очки на баланс счета платформы FMZ в соотношении 10:1, а пользователи также могут использовать очки для обмена соответствующих продуктов FMZ Quant в будущем.https://www.fmz.com/bbs-topic/3828

Квантовая платформа FMZ для предприятий

Полный веб-сайт FMZ может быть развернут на эксклюзивном сервере предприятия или команды для полного контроля и функциональной настройки. Веб-сайт FMZ использовался и тестировался примерно 100 000 пользователей и достиг высокой доступности и безопасности, что может сэкономить время для количественных команд и предприятий. Предприятия версии предназначены для средних количественных торговых команд, поставщиков товарных фьючерсных услуг и т. Д. Пожалуйста, свяжитесь с администратором для конкретных цитат.

Система создания рынка

Профессиональная система, обеспечивающая рыночную ликвидность и управление средствами для платформ, может быть наиболее совершенной системой создания рынка на рынке.

Схема платформы

Система торговли технологией FMZ использует технологию сопоставления памяти, и скорость обработки заказов достигает 2 миллионов сделок в секунду, что может гарантировать отсутствие задержки или задержки в обработке заказов. Она может поддерживать плавную и стабильную работу платформ с более чем 20 миллионами одновременных онлайн-пользователей. Многослойная и многокластерная система обеспечивает безопасность, стабильность и расширяемость системы. Развертывание функций и обновления версий могут осуществляться без простоев, что максимально гарантирует операционный опыт пользователей терминала.


Больше