Tutorial Lanjutan untuk Platform FMZ Quant Strategy Writing

Penulis:Ninabadass, Dicipta: 2022-03-22 09:00:57, Dikemas kini: 2022-03-29 10:02:52

[TOC] Sebelum belajar tutorial ini, anda perlu belajarMulakan dengan Platform FMZ QuantdanTutorial asas untuk platform FMZ Quant Strategy Writing, dan menguasai bahasa pengaturcaraan.Tutorial asas merangkumi fungsi yang paling biasa digunakan, tetapi terdapat banyak fungsi dan ciri yang belum diperkenalkan, dan mereka tidak akan dilindungi dalam tutorial ini.Selepas mempelajari tutorial ini, anda akan dapat menulis lebih banyak strategi percuma dan disesuaikan, dan platform FMZ Quant hanyalah alat.

Akses kepada Data mentah Platform

Platform FMZ Quant merangkumi semua platform yang disokong. Untuk mengekalkan keseragaman, sokongan kami untuk API platform tunggal masih tidak lengkap. Sebagai contoh, GetRecords boleh menyampaikan jumlah K-line atau masa permulaan, sementara ia tetap pada platform FMZ; beberapa platform menyokong pesanan batch, sementara FMZ tidak menyokongnya, dan sebagainya. Jadi terdapat keperluan untuk cara untuk mengakses data platform secara langsung.Untuk antara muka awam (seperti sebut harga pasaran), anda boleh menggunakanHttpQuery, dan untuk antara muka yang disulitkan (yang melibatkan maklumat akaun), anda perlu menggunakanIO.Untuk parameter masuk tertentu, sila rujuk dokumen API platform yang sepadan.Infomedan mengembalikan maklumat mentah, tetapi ia masih tidak membuat perbezaan pada masalah tidak menyokong antara muka.

GetRawJSON ((()

Ia mengembalikan kandungan mentah (string) yang diminta oleh API REST terakhir, yang boleh digunakan untuk menganalisis maklumat lanjutan dengan sendirinya.

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() Akses Antara muka awam

Untuk mengakses antara muka awam, Js boleh menggunakanHttpQuery, dan Python boleh menggunakan pakej yang berkaitan, sepertiurllibataurequests.

HttpQuery lalai kepada kaedah GET, dan menyokong lebih banyak fungsi; lihat dokumen API untuk maklumat lanjut.

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"))

Contoh Python menggunakan permintaan:

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 mengakses antara muka yang disulitkan

Untuk antara muka yang memerlukan tandatangan API-KEY, fungsi IO boleh digunakan, dan pengguna hanya perlu mengambil berat tentang parameter masuk, dan proses tandatangan tertentu akan diselesaikan oleh lapisan bawah.

Platform FMZ pada masa ini tidak menyokong pesanan stop-loss BitMEX, yang boleh dilaksanakan melalui IO, mengikut langkah-langkah berikut:

  • Pertama, cari halaman arahan BitMEX API:https://www.bitmex.com/api/explorer/;
  • Kemudian, cari alamat pesanan BitMEX di:https://www.bitmex.com/api/v1/order, dengan kaedahPOST; untuk FMZ telah menentukan alamat pangkalan secara dalaman, anda hanya perlu menghantar /api/v1/order.
  • parameter yang sepadan:symbol=XBTUSD&side=Buy&orderQty=1&stopPx=4000&ordType=Stop.

Kod khusus:

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"}))

Lebih banyak contoh IO:https://www.fmz.com/bbs-topic/3683

Menggunakan websocket

Pada asasnya, semua platform cryptocurrency menyokong websocket untuk menghantar sebut harga pasaran, dan beberapa platform menyokong websocket untuk mengemas kini maklumat akaun. Berbanding dengan API rehat, websocket umumnya mempunyai kelebihan, seperti latensi rendah, frekuensi tinggi dan tidak terhad oleh frekuensi API rehat platform, dll. Kelemahannya adalah bahawa terdapat masalah gangguan, yang pemprosesannya tidak intuitif.

Artikel ini terutamanya akan memperkenalkan cara menggunakan bahasa JavaScript dan cara menggunakan fungsi Dial yang dikapsulkan oleh platform untuk menyambung, pada platform FMZ Quant; untuk arahan dan parameter tertentu terdapat dalam dokumen, anda boleh mencari Dial; untuk merealisasikan pelbagai fungsi, fungsi Dial telah dikemas kini beberapa kali. Artikel ini akan merangkumi dan memperkenalkan strategi yang didorong peristiwa berdasarkan wss, serta isu menyambung beberapa platform. Python juga boleh menggunakan fungsi Dial, atau perpustakaan yang sesuai.

1. Sambungan Websocket

Secara amnya, menyambung secara langsung melalui Websocket; contohnya untuk mendapatkan dorongan penipu Binance:

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

Jika data yang dikembalikan adalah dalam format yang dimampatkan, spesifikasi harus dibuat semasa menyambung; compress merujuk kepada format yang dimampatkan, dan mode mewakili data yang dikembalikan perlu dimampatkan; contohnya, apabila menyambung dengan OKEX:

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

Fungsi Dial menyokong sambungan semula, yang dilakukan oleh Golang yang mendasari. Jika sambungan yang dikesan rosak, ia akan disambungkan semula. Untuk data permintaan yang sudah ada di url, seperti contoh Binance sekarang, ia sangat mudah dan disyorkan. Bagi mereka yang perlu menghantar mesej langganan, mereka boleh mengekalkan mekanisme sambungan semula sendiri.

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

Untuk melanggan mesej wss, beberapa permintaan platform berada di url, dan beberapa memerlukan untuk menghantar saluran yang melanggan sendiri, seperti coinbase:

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

2.Sambungan antara muka yang disulitkan

Secara amnya, websocket digunakan untuk membaca sebut harga pasaran, tetapi ia juga boleh digunakan untuk mendapatkan pesanan dan memaksa akaun. Memaksa data yang disulitkan kadang-kadang mempunyai kelewatan yang panjang dan harus digunakan dengan berhati-hati. Oleh kerana kaedah penyulitan lebih rumit, berikut adalah beberapa contoh yang diberikan untuk rujukan. Perhatikan bahawa hanya AccessKey diperlukan, yang boleh ditetapkan sebagai parameter strategi. Jika SecretKey diperlukan, ia boleh dipanggil secara tersirat oleh fungsi pertukaran.HMAC() untuk memastikan keselamatan.

    //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.Websocket Baca

Secara amnya, ia boleh dibaca secara berterusan dalam gelung yang tidak berkesudahan.

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 
    }
}

kelajuan push data wss sangat cepat. lapisan bawah Golang akan cache semua data dalam antrian, dan apabila panggilan program dibaca, data akan dikembalikan. Walau bagaimanapun, operasi seperti meletakkan pesanan pada bot akan menyebabkan kelewatan, yang mungkin mengakibatkan pengumpulan data. Untuk maklumat seperti dorongan pelaksanaan perdagangan, dorongan akaun, dan dorongan interpolasi kedalaman, kita memerlukan data sejarah. Untuk data pasaran sebut harga, dalam kebanyakan kes, kita hanya peduli dengan data terkini, bukan data sejarah.

Jikaread()jika tidak menambah parameter, ia akan mengembalikan data tertua, dan blok sehingga kembali apabila tidak ada data.client.read(-2)untuk mengembalikan data terkini dengan segera, tetapi apabila tidak ada data, ia akan mengembalikan null, yang perlu dinilai sebelum rujukan.

Bergantung pada cara menangani data cache lama dan sama ada ia disekat apabila tidak ada data, read mempunyai parameter yang berbeza, seperti yang ditunjukkan dalam jadual di bawah, yang kelihatan rumit, tetapi menjadikan program lebih fleksibel.img

4.Sambungan dengan pelbagai platform oleh Websocket

Dalam kes ini, adalah jelas bahawa hanya menggunakan read() tidak berfungsi dalam program, kerana satu platform akan menyekat mesej menunggu, dan platform lain tidak akan menerima walaupun terdapat mesej baru.

    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.Masalah Penghentian & Sambungan Kembali

Bahagian pemprosesan ini lebih menyusahkan, kerana data push mungkin terganggu, atau kelewatan push sangat lama. Walaupun detak jantung dapat diterima, itu tidak bermakna data masih didorong. Anda boleh menetapkan selang acara; jika tidak ada kemas kini yang diterima selepas selang waktu, sambung semula; adalah lebih baik untuk membandingkan hasil yang dikembalikan oleh rest selepas tempoh masa, untuk melihat sama ada data itu tepat. Untuk kes khas Binance, anda boleh secara langsung menetapkan sambungan semula automatik.

6.Menggunakan Bingkai Program Umum Websocket

Untuk data dorong telah digunakan, program secara semula jadi akan ditulis sebagai acara-dihidupkan; memberi perhatian kepada kekerapan data dorong, kerana permintaan frekuensi tinggi akan menyebabkan disekat; secara amnya anda boleh menulis:

    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

Kaedah sambungan, kaedah penghantaran data, kandungan langganan dan format data websocket pada setiap platform sering berbeza, jadi platform tidak mengkapsulkannya dan perlu menggunakan fungsi Dial untuk menyambung sendiri.

PS: Walaupun beberapa platform tidak menyediakan petikan websocket, sebenarnya, apabila anda log masuk ke laman web untuk menggunakan fungsi debugging, anda akan mendapati bahawa mereka semua menggunakan websocket push.

Persaingan Multithread

JavaScript boleh merealisasikan kecocokan oleh fungsi Go, dan Python boleh menggunakan perpustakaan multithread yang sepadan.

Semasa merealisasikan strategi kuantitatif, pelaksanaan serentak dapat mengurangkan kelewatan masa dan meningkatkan kecekapan. Ambil bot strategi lindung nilai sebagai contoh. Ia perlu mendapatkan kedalaman dua syiling, dan kod yang dilaksanakan mengikut urutan ditunjukkan sebagai:

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

Apabila permintaan API rehat ditangguhkan, contohnya masa kelewatan adalah 100 milisaat, maka masa untuk mendapatkan kedalaman dua kali sebenarnya berbeza; jika lebih banyak akses diperlukan, masalah kelewatan akan lebih jelas, yang akan mempengaruhi pelaksanaan strategi.

Oleh kerana JavaScript tidak mempunyai multithread, lapisan bawah merangkumi fungsi Go untuk menyelesaikan masalah ini. fungsi Go boleh digunakan untuk API yang memerlukan akses rangkaian, sepertiGetDepth, GetAccountdan seterusnya.IOjuga disokong, seperti:exchange.Go("IO", "api", "POST", "/api/v1/contract_batchorder", "orders_data=" + JSON.stringify(orders)), tetapi kerana mekanisme reka bentuk, ia lebih membosankan untuk dilaksanakan.

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()

Dalam kebanyakan kes yang mudah, menulis strategi dengan cara ini baik-baik saja. Tetapi perhatikan bahawa proses itu diulangi setiap kali strategi gelung, dan pemboleh ubah perantara a dan b sebenarnya hanya sementara tambahan. Jika kita mempunyai banyak tugas serentak, kita perlu tambahan merakam korespondensi antara a dan kedalamanA, b dan kedalamanB. Apabila tugas serentak kita tidak pasti, situasi lebih rumit. Oleh itu, kita berharap untuk merealisasikan fungsi: apabila menulis fungsi Go serentak, mengikat pemboleh ubah pada masa yang sama; apabila hasil berjalan serentak kembali, nilai hasil secara automatik diberikan kepada pemboleh ubah, dengan itu menghapuskan keperluan untuk membuat pemboleh ubah perantara dan program lebih ringkas. Pelaksanaan khusus adalah seperti berikut:

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

Kami menentukan fungsi G, di mana parameter t adalah fungsi Go yang akan dijalankan, ctx adalah fungsi rekod konteks program, dan f adalah fungsi yang menetapkan nilai tertentu.

Pada masa ini, kerangka kerja program keseluruhan boleh ditulis sebagai model, serupa dengan model pengeluar-pengguna (dengan beberapa perbezaan), pengeluar terus menghantar tugas, dan pengguna mengeksekusi mereka secara serentak.

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)
    }
}

Ia kelihatan bahawa hanya fungsi mudah telah dilaksanakan dalam operasi di atas. Sebenarnya, yang telah sangat memudahkan kerumitan kod. Kita hanya perlu mengambil berat tentang apa tugas yang perlu dihasilkan oleh program, dan worker() program akan secara automatik melaksanakan mereka serentak dan mengembalikan hasil yang sepadan. fleksibiliti telah bertambah baik banyak.

Menggambar mengikut fungsi carta

Dalam tutorial asas, perpustakaan kelas lukisan disyorkan dalam pengenalan lukisan, yang dapat memenuhi keperluan dalam kebanyakan kes.

Parameter dalamanChart({…})adalah objek HighStock dan HighCharts, tetapi parameter tambahan__isStockFMZ pada dasarnya menyokong modul asas HighCharts dan HighStock, tetapi tidak menyokong modul tambahan.

Contoh HighCharts khusus:https://www.highcharts.com/demo; Contoh HighStock:https://www.highcharts.com/stock/demo. Anda boleh merujuk kepada kod dalam contoh-contoh itu, dan memindahkan mereka ke FMZ mudah.

Anda boleh memanggil add ([indeks siri ((seperti 0), data]) untuk menambah data ke dalam siri dengan indeks yang ditentukan. Panggil reset() untuk membersihkan data carta; reset boleh mengambil parameter nombor dan menentukan jumlah yang akan disimpan. Penampilan carta berbilang disokong, yang hanya perlu lulus dalam parameter array semasa konfigurasi, seperti: var chart = Chart (([{...}, {...}, {...}]). Sebagai contoh, jika Carta1 mempunyai dua siri, Carta2 mempunyai satu siri, dan Carta3 mempunyai satu siri, apabila memanggil add, ID siri 0 dan 1 ditentukan untuk mewakili data kedua siri secara berasingan dalam Carta1 yang dikemas kini; siri 2 ditentukan untuk mewakili data siri pertama dalam Carta2; siri ID 3 ditentukan untuk mewakili siri pertama dalam Carta3.

Contoh khusus:

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);
    }
}

Contoh penggunaan susun atur carta:https://www.fmz.com/strategy/136056

Ujian Backtest Lanjutan

Python Lokal Backtest

Alamat sumber terbuka khusus:https://github.com/fmzquant/backtest_python

Pemasangan

Masukkan arahan berikut dalam baris arahan:

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

Contoh Sederhana

Tetapkan parameter backtest di awal kod strategi dalam bentuk komen, dan butiran akan dipaparkan di butang Simpan Tetapan di halaman Sunting Strategi laman web 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

Ujian belakang

Untuk strategi yang lengkap memerlukan gelung tanpa akhir, kesilapan EOF akan dibangkitkan selepas backtest selesai; oleh itu, kita harus melakukan toleransi ralat dengan baik.

# !/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()         

Data Backtest tersuai

exchange.SetData(arr) menukar sumber data backtest dan menggunakan data K-line tersuai. Parameter arr adalah array, yang unsur-unsurnya adalah data bar K-line (iaitu: array data K-line, yang sementara hanya menyokong backtest JavaScript.

dalam array arr, format data elemen tunggal:

[
    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 
]

Sumber data boleh diimport dalam Contoh.

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")
}

Nota: pastikan untuk mengimport data tersuai terlebih dahulu (iaitu, panggil fungsi exchange.SetData untuk menetapkan data) semasa inisialisasi. Tempoh data K-garis tersuai mestilah konsisten dengan tempoh K-garis bawah yang ditetapkan pada halaman backtest, iaitu: data K-garis tersuai; masa K-garis adalah 1 minit, jadi tempoh K-garis bawah yang ditetapkan dalam backtest juga harus ditetapkan menjadi 1 minit.

Menggunakan Platform yang Tidak Disokong oleh FMZ

Jika API platform yang tidak disokong adalah sama dengan platform yang disokong, kecuali alamat pangkalan, platform yang tidak disokong boleh disokong dengan menukar alamat pangkalan. Untuk lebih spesifik, pilih platform yang disokong apabila menambah platform, tetapi isi API-KEY platform yang tidak disokong, dan gunakan IO untuk menukar alamat pangkalan dalam strategi, seperti:

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

Tidak semua platform disokong oleh FMZ, tetapi platform kami telah menyediakan kaedah akses protokol umum.

  • Apabila anda menulis kod untuk mengakses platform, program akan mencipta perkhidmatan web.
  • Apabila anda menambah platform ke FMZ, tentukan alamat dan port perkhidmatan web.
  • Apabila docker menjalankan bot platform protokol umum, permintaan akses API dalam strategi akan dihantar ke protokol umum.
  • Protokol umum akan mengakses platform dengan permintaan, dan mengembalikan hasilnya kepada docker.

Secara ringkasnya, protokol umum adalah seperti perantara, melampirkan permintaan docker dan mengembalikan data, mengikut standard yang sesuai. Kod protokol umum perlu disiapkan sendiri. Menulis protokol umum sebenarnya bermakna anda boleh mengakses platform semata-mata dan menyelesaikan strategi. FMZ rasmi kadang-kadang mengeluarkan versi exe protokol umum platform. Protokol umum juga boleh dilakukan dalam Python, yang kemudian boleh dijalankan di docker sebagai bot biasa.

Pengenalan khusus protokol umum:https://www.fmz.com/bbs-topic/9120Contoh penulisan protokol umum dalam Python:https://www.fmz.com/strategy/101399

Mencipta Platform Kuantitatif Anda Sendiri

Sama seperti pelbagai operasi platform boleh dilaksanakan melalui API, laman web FMZ juga berdasarkan API. Anda boleh memohon API-KEY laman web FMZ anda sendiri untuk merealisasikan fungsi, seperti create, restart, DeleteRobot, GetRobotList GetRobotLogs, dll. Sila rujuk bahagian API Extension of FMZ Platform dalam dokumen API untuk butiran.

Oleh kerana kebolehluasan kuat platform FMZ Quant, anda boleh membuat platform kuantitatif anda sendiri berdasarkan sambungan API, membolehkan pengguna anda menjalankan bot di platform anda, dll. Rujukan khusus:https://www.fmz.com/bbs-topic/1697 .

Menjadi Rakan FMZ

Promosi Kelas NetEase

Pasaran perdagangan mata wang kripto telah menarik lebih banyak perhatian daripada peniaga kuantitatif kerana keunikannya. Sebenarnya, perdagangan program telah menjadi arus perdana mata wang kripto, dan strategi seperti lindung nilai dan pembuatan pasaran selalu aktif di pasaran. Pemula dengan asas pengaturcaraan yang lemah ingin memasuki bidang baru ini, berhadapan dengan banyak platform dan API yang berubah, penuh dengan kesukaran.www.fmz.comUntuk mengambil kursus di NetEase, anda hanya memerlukan 20 yuan, dan kursus ini sepenuhnya untuk pemula.

PemberdayaanKursus Perdagangan Kuantitatif Cryptocurrency di NetEase Cloud Classroom. Log masuk ke NetEase Cloud Classroom dan kongsi pautan kursus anda (pautan ini mempunyai ID kursus yang unik). Yang lain, yang mendaftar dan membeli kursus melalui pautan ini, akan membawa anda 50% daripada jumlah keseluruhan sebagai komisen, iaitu 10 yuan. Ikuti akaun awam WeChat NetEase Cloud Classroom Premium Course Promotion untuk mengeluarkan wang tunai. Anda juga dialu-alukan untuk menjemput orang lain untuk mempromosikan kursus di Weibo atau kumpulan QQ.

Berkaitan

Pengguna yang mengklik pautan promosi, mendaftar dan mengecas semula dalam tempoh setengah tahun, akan menikmati dasar bahawa syarikat kami akan memberi diskaun mengikut jumlah yang berkesan dalam pesanan yang sah. Suruhanjaya akan dikembalikan ke akaun promoter dalam bentuk mata. Pengguna boleh menukar mata ke baki akaun platform FMZ pada nisbah 10: 1, dan pengguna juga boleh menggunakan mata untuk menukar produk berkaitan FMZ Quant pada masa akan datang. Pautan khusus untuk aktiviti:https://www.fmz.com/bbs-topic/3828

Platform Kuantum FMZ untuk Syarikat

Laman web FMZ lengkap boleh digunakan pada pelayan eksklusif perusahaan atau pasukan untuk kawalan lengkap dan penyesuaian fungsi. Laman web FMZ telah digunakan dan diuji oleh kira-kira 100,000 pengguna, dan telah mencapai ketersediaan dan keselamatan yang tinggi, yang dapat menjimatkan masa untuk pasukan kuantitatif dan perusahaan. Versi perusahaan adalah untuk pasukan perdagangan kuantitatif bersaiz sederhana, penyedia perkhidmatan niaga hadapan komoditi, dll. Sila hubungi pentadbir untuk sebut harga khusus.

Sistem Pembentukan Pasaran

Sistem profesional, menyediakan kecairan pasaran dan pengurusan dana untuk platform, mungkin merupakan sistem pembuatan pasaran yang paling dipertingkatkan di pasaran.

Skim Platform

Sistem perdagangan teknologi FMZ mengamalkan teknologi pencocokan memori, dan kelajuan pemprosesan pesanan setinggi 2 juta dagangan sesaat, yang dapat memastikan bahawa tidak akan ada kelewatan atau kelewatan dalam pemprosesan pesanan. Ia dapat mengekalkan operasi yang lancar dan stabil platform dengan lebih daripada 20 juta pengguna dalam talian serentak. Kerangka sistem pelbagai lapisan dan pelbagai klaster memastikan keselamatan, kestabilan dan kebolehluasan sistem. Pengerahan fungsi dan kemas kini versi dapat dilakukan tanpa downtime, yang menjamin pengalaman operasi pengguna terminal maksimum. Pada masa ini, sistem dapat dialami dalam platform simulasi wex.app.


Lebih lanjut