3
ध्यान केंद्रित करना
1444
समर्थक

FMZ मात्रात्मक प्लेटफ़ॉर्म रणनीति लेखन उन्नत ट्यूटोरियल

में बनाया: 2019-08-19 15:54:35, को अपडेट: 2021-06-08 16:14:57
comments   8
hits   32175

[TOC] इस ट्यूटोरियल को सीखने से पहले, आपको सीखना होगाएफएमजेड आविष्कारक के लिए क्वांटिटेटिव प्लेटफॉर्म का उपयोग करनाऔरएफएमजेड क्वांटिफाइंग प्लेटफॉर्म रणनीति के लिए प्रारंभिक ट्यूटोरियलऔर प्रोग्रामिंग भाषाओं का उपयोग करने में कुशल।शुरुआती ट्यूटोरियल में सबसे अधिक उपयोग किए जाने वाले फ़ंक्शंस शामिल हैं, लेकिन कई अन्य फ़ंक्शंस और सुविधाएं हैं जो इस ट्यूटोरियल में शामिल नहीं हैं।इस ट्यूटोरियल को पूरा करने के बाद, आप अधिक स्वतंत्र, अधिक अनुकूलित रणनीतियों को लिखने में सक्षम होंगे, और FMZ प्लेटफॉर्म केवल एक उपकरण है।

एक्सचेंजों के कच्चे डेटा तक पहुंच

एफएमजेड प्लेटफॉर्म सभी समर्थित एक्सचेंजों के लिए पैकेज किया गया है, और एकरूपता बनाए रखने के लिए, एकल एक्सचेंज एपीआई समर्थन पूर्ण नहीं है। जैसे कि K लाइनों की संख्या या आरंभिक समय में प्रवेश करना, जबकि एफएमजेड प्लेटफॉर्म निश्चित है, कुछ प्लेटफॉर्म थोक आदेश का समर्थन करते हैं, एफएमजेड समर्थित नहीं है, आदि। इसलिए एक्सचेंज डेटा तक सीधे पहुंचने के लिए एक विधि की आवश्यकता है।सार्वजनिक इंटरफेस के लिए (यदि लागू हो)HttpQueryइस प्रकार, एक बार खाता खोलने के बाद, उपयोगकर्ता को अपने खाते के बारे में जानकारी प्राप्त करने की आवश्यकता होती है।IOविशिष्ट इनपुट मापदंडों के लिए, संबंधित एक्सचेंज एपीआई दस्तावेज़ देखें। पिछले ट्यूटोरियल में बताया गया है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 का उपयोग करने के लिए एक सार्वजनिक इंटरफ़ेस का उपयोग करेंHttpQueryPython अपने आप से संबंधित पैकेजों का उपयोग कर सकता है, जैसे किurllibयाrequests

HttpQuery डिफ़ॉल्ट रूप से GET विधि है, और अधिक सुविधाओं के लिए समर्थन करता है, एपीआई प्रलेखन देखें।

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

पायथन में अनुरोधों का उदाहरण

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

आईओ फ़ंक्शन के पास

API-KEY हस्ताक्षर की आवश्यकता वाले इंटरफेस के लिए, IO फ़ंक्शन का उपयोग किया जा सकता है, उपयोगकर्ता को केवल इनपुट पैरामीटर की परवाह करने की आवश्यकता होती है, और हस्ताक्षर प्रक्रिया नीचे की ओर से की जाती है।

FMZ प्लेटफ़ॉर्म वर्तमान में BitMEX स्टॉपलॉस का समर्थन नहीं करता है, निम्नलिखित चरणों के अनुसार IO के माध्यम से लागू करें:

  • पहले BitMEX के एपीआई इंटरफेस के बारे में पढ़ेंः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")
//也可以直接传入对象
var id = exchange.IO("api", "POST", "/api/v1/order", "", JSON.stringify({symbol:"XBTUSD",side:"Buy",orderQty:1,stopPx:4000,ordType:"Stop"}))

अधिक आईओ उदाहरणः https://www.fmz.com/bbs-topic/3683

वेबसॉकेट का उपयोग करना

मूल रूप से सभी डिजिटल मुद्रा एक्सचेंज वेबसॉकेट भेजने की स्थिति का समर्थन करते हैं, कुछ एक्सचेंज वेबसॉकेट अपडेट खाता जानकारी का समर्थन करते हैं। रेस्ट एपीआई की तुलना में, वेबसॉकेट में आमतौर पर कम विलंबता, उच्च आवृत्ति होती है, प्लेटफ़ॉर्म रेस्ट एपीआई आवृत्ति प्रतिबंधों से मुक्त होती है।

इस लेख में मुख्य रूप से एफएमजेड के आविष्कारक की मात्रा के बारे में बताया जाएगा, जावास्क्रिप्ट भाषा का उपयोग करें, डायल फ़ंक्शन का उपयोग करें जो प्लेटफ़ॉर्म के साथ संलग्न है, दस्तावेज़ में विनिर्देश और पैरामीटर, डायल खोजें, विभिन्न कार्यों को लागू करने के लिए डायल फ़ंक्शन को कई बार अपडेट किया गया है, इस लेख में इसे कवर किया जाएगा, और डब्ल्यूएसएस-आधारित इवेंट-ड्राइव रणनीतियों के बारे में बताया जाएगा, साथ ही साथ कई एक्सचेंजों को जोड़ने की समस्या। पायथन डायल फ़ंक्शन का भी उपयोग कर सकता है, और संबंधित पुस्तकालयों का भी उपयोग कर सकता है।

1. वेबसॉकेट कनेक्शन

आम तौर पर, यह सीधे कनेक्ट करने के लिए होता है, जैसे कि सिक्का सुरक्षा टिकर भेजने के लिएः

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

रिटर्न डेटा के लिए संपीड़न प्रारूप है, जो कनेक्शन में निर्दिष्ट किया जाना चाहिए, compress संपीड़न प्रारूप निर्दिष्ट करें, और मोड रिटर्न डेटा भेजने के लिए प्रतिनिधित्व करता है जिसे संपीड़न की आवश्यकता है, जैसे कि OKEX कनेक्शनः

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

डायल फ़ंक्शन का समर्थन करता है, जो कि अंतर्निहित गो भाषा द्वारा किया जाता है, जो पता लगाता है कि कनेक्शन टूट गया है और फिर से जोड़ा गया है, अनुरोध डेटा सामग्री पहले से ही यूआरएल में है, जैसे कि हाल ही में बिनान का उदाहरण, उपयोग करने के लिए सिफारिश की गई है। आदेश संदेश भेजने की आवश्यकता के लिए, आप स्वयं को फिर से जोड़ने की व्यवस्था रख सकते हैं।

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

कुछ एक्सचेंजों के अनुरोध यूआरएल में हैं, और कुछ चैनलों को स्वयं सदस्यता की आवश्यकता होती है, जैसे कि coinbase:

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

2. निकट संपर्क जोड़ें

आम तौर पर वेबसॉकेट को पढ़ने के लिए उपयोग किया जाता है, लेकिन इसे ऑर्डर प्राप्त करने के लिए भी इस्तेमाल किया जा सकता है। इस तरह के एन्क्रिप्टेड डेटा को आगे बढ़ाने में कभी-कभी बहुत देरी होती है, इसलिए सावधानी बरतने की आवश्यकता होती है। क्योंकि एन्क्रिप्शन विधि अधिक जटिल है, यहां कुछ उदाहरण दिए गए हैं। ध्यान दें कि केवल एक्सेसकी की आवश्यकता होती है, जिसे नीति पैरामीटर के रूप में सेट किया जा सकता है, जैसे कि गुप्तकी की आवश्यकता होती है, एक्सचेंज.एचएमएसी () फ़ंक्शन को गुप्त रूप से बुलाया जा सकता है, सुरक्षा की गारंटी देता है।

    //火币期货推送例子
    var ACCESSKEYID = '你的火币账户的accesskey'
    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} }") //去掉}}之间的多余空格
    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}))
        }
    }
    
    //币安推送例子,注意需要定时更新listenKey
    var APIKEY = '你的币安accesskey'
    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()
    }

    //BitMEX推送例子
    var APIKEY = "你的Bitmex API ID"
    var expires = parseInt(Date.now() / 1000) + 10
    var signature = exchange.HMAC("sha256", "hex", "GET/realtime" + expires, "{{secretkey} }")//secretkey在执行时自动替换,不用填写
    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 पढ़ता है

सामान्यतः मृत चक्र में लगातार पढ़ा जा सकता है, कोड इस प्रकार है:

function main() {
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
    while (true) {
        var msg = client.read()
        var data = JSON.parse(msg) //把json字符串解析为可引用的object
// 处理data数据
    }
}

डब्ल्यूएसएस डेटा तेजी से आगे बढ़ रहा है, गो के निचले स्तर पर सभी डेटा को कतार में कैश किया जाता है, जैसे कि रीड को कॉल करते समय, और फिर वापस आते हैं। जबकि रीयल-डिस्क पर ऑर्डर जैसे ऑपरेशन देरी का कारण बनते हैं, जिससे डेटा का संचय हो सकता है। लेनदेन के लिए आगे बढ़ना, खाता आगे बढ़ना, गहराई से आगे बढ़ना आदि, हमें ऐतिहासिक डेटा की आवश्यकता होती है, वास्तविक डेटा के लिए, हम ज्यादातर मामलों में केवल नवीनतम की परवाह करते हैं, ऐतिहासिक डेटा की परवाह नहीं करते हैं।

read()यदि कोई पैरामीटर नहीं है, तो यह सबसे पुराना डेटा लौटाता है, और यदि कोई डेटा नहीं है तो इसे वापस करने के लिए अवरुद्ध कर दिया जाता है। यदि आप नवीनतम डेटा चाहते हैं, तो आप इसका उपयोग कर सकते हैंclient.read(-2)यह तुरंत नवीनतम डेटा लौटाता है, लेकिन जब कोई और डेटा नहीं होता है, तो इसे फिर से संदर्भित करने के लिए निर्णय लेने की आवश्यकता होती है।

read में विभिन्न पैरामीटर होते हैं, जो कैश किए गए पुराने डेटा के साथ कैसे व्यवहार करते हैं, और क्या यह बिना डेटा के अवरुद्ध हो जाता है, जैसा कि नीचे दिखाया गया है, जो जटिल दिखता है, लेकिन प्रोग्राम को अधिक लचीला बनाता है। FMZ मात्रात्मक प्लेटफ़ॉर्म रणनीति लेखन उन्नत ट्यूटोरियल

4. कई एक्सचेंजों के वेबसॉकेट को जोड़ना

इस स्थिति के लिए प्रक्रिया में स्पष्ट रूप से सरल 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) // 参数-1代表无数据立即返回null,不会阻塞到有数据返回
            var msgCoinbase = coinbase.read(-1)
            if(msgBinance){
                // 此时币安有数据返回
            }
            if(msgCoinbase){
                // 此时coinbase有数据返回
            }
            Sleep(1) // 可以休眠1ms
        }
    }

5. डिस्कनेक्ट की समस्या

इस भाग को संभालना थोड़ा मुश्किल है, क्योंकि डेटा को आगे बढ़ाया जा सकता है, या देरी बहुत अधिक है, भले ही दिल की धड़कन प्राप्त करने का मतलब यह नहीं है कि डेटा अभी भी आगे बढ़ रहा है, एक घटना अंतराल सेट किया जा सकता है, यदि अंतराल से अधिक अपडेट प्राप्त नहीं किया गया है, तो फिर से कनेक्ट करें, और यह देखने के लिए कि क्या डेटा सटीक है या नहीं, यह देखने के लिए कि कुछ समय के बाद और बाकी के परिणामों की तुलना करना सबसे अच्छा है। इस विशेष स्थिति के लिए, सीधे स्वचालित रूप से फिर से कनेक्ट करें।

6. वेबसॉकेट का उपयोग करने के लिए सामान्य प्रोग्रामिंग फ्रेमवर्क

चूंकि पुश डेटा का उपयोग किया गया है, इसलिए प्रोग्राम को स्वाभाविक रूप से इवेंट ड्राइव के रूप में लिखा जाना चाहिए, ध्यान दें कि पुश डेटा अक्सर होता है, बहुत अधिक अनुरोधों के बिना बंद हो जाता है, आमतौर पर इसे लिखा जा सकता हैः

    var tradeTime = Date.now()
    var accountTime = Date.now()
    function trade(data){
        if(Date.now() - tradeTime > 2000){//这里即限制了2s内只交易一次
            tradeTime = Date.now()
            //交易逻辑
        }
    }
    function GetAccount(){
        if(Date.now() - accountTime > 5000){//这里即限制了5s内只获取账户一次
            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. सारांश

विभिन्न एक्सचेंजों के वेबसॉकेट का कनेक्शन तरीका, डेटा भेजने का तरीका, सदस्यता योग्य सामग्री, डेटा प्रारूप अक्सर अलग-अलग होते हैं, इसलिए प्लेटफ़ॉर्म को पैकेज नहीं किया जाता है, डायल फ़ंक्शन का उपयोग करके स्वचालित रूप से कनेक्ट करने की आवश्यकता होती है। यह लेख कुछ बुनियादी सावधानियों को कवर करता है, यदि कोई प्रश्न है, तो पूछने के लिए आपका स्वागत है।

PS. कुछ एक्सचेंजों ने वेबसॉकेट ट्रेडिंग की पेशकश नहीं की है, लेकिन वास्तव में वेबसॉकेट पुशिंग का उपयोग करके साइट पर लॉग इन किया गया है, जो सब्सक्रिप्शन प्रारूप और रिटर्न प्रारूप का उपयोग करता है। कुछ ऐसा लगता है कि यह एन्क्रिप्टेड है और इसे base64 डिकोड और डिकंप्रेस किया जा सकता है।

बहु-थ्रेड समवर्ती

जावास्क्रिप्ट को Go फ़ंक्शन के माध्यम से समानांतर किया जा सकता है, और पायथन को इसके अनुरूप बहु-थ्रेड लाइब्रेरी का उपयोग करके बनाया जा सकता है।

एक परिमाणित रणनीति को लागू करते समय, कई मामलों में, समवर्ती निष्पादन विलंबता को कम कर सकता है। उदाहरण के लिए, एक हेजिंग रणनीति के लिए, दो सिक्के की गहराई प्राप्त करने की आवश्यकता होती है, क्रमिक निष्पादन कोड निम्नानुसार हैः

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

रेस्ट एपीआई का अनुरोध करने में देरी होती है। मान लें कि यह 100ms है। फिर गहराई को दो बार प्राप्त करने का समय वास्तव में अलग है। यदि अधिक पहुंच की आवश्यकता है, तो देरी की समस्या अधिक प्रमुख होगी, जो रणनीति के निष्पादन को प्रभावित करेगी।

जावास्क्रिप्ट में कोई मल्टी-थ्रेड नहीं है, इसलिए Go फ़ंक्शन को इस समस्या को हल करने के लिए अंतर्निहित किया गया है। Go फ़ंक्शन का उपयोग उन एपीआई के लिए किया जा सकता है जिन्हें नेटवर्क एक्सेस की आवश्यकता होती है, जैसे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() //调用wait方法等待返回异步获取depth结果 
var depthB = b.wait()

अधिकांश सरल मामलों में, इस प्रकार नीति लिखने में कोई गलती नहीं है। लेकिन ध्यान रखें कि इस प्रक्रिया को हर बार रणनीति लूप के दौरान दोहराया जाना चाहिए, और मध्यवर्ती चर a और b वास्तव में केवल अस्थायी सहायता हैं। यदि हमारे पास बहुत सारे समवर्ती कार्य हैं, तो हमें a औरdepthA, तथा b औरdepthB के बीच पत्राचार रिकॉर्ड करने की आवश्यकता है। जब हमारे समवर्ती कार्य अनिश्चित होते हैं, तो स्थिति अधिक जटिल हो जाती है। इसलिए, हम एक फ़ंक्शन को लागू करना चाहते हैं: जब गो समवर्ती लिखते हैं, तो एक ही समय में एक चर को बांधें, और जब समवर्ती चल रहे परिणाम को वापस किया जाता है, तो परिणाम स्वचालित रूप से चर को सौंपा जाता है, इस प्रकार मध्यवर्ती चर को समाप्त कर देता है और कार्यक्रम को अधिक बनाता है संक्षिप्त. विशिष्ट कार्यान्वयन इस प्रकार है:

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}] //加入我们需要获取两个交易所的深度和账户,跟多的信息也可以放入,如订单Id,状态等。
var tasks = [ ] //全局的任务列表

function produce(){ //下发各种并发任务
  //这里省略了任务产生的逻辑,仅为演示
  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 //这里的v就是并发Go函数wait()的返回值,可以仔细体会下
                }))
    }
    _.each(jobs, function(t){
            t.run() //在这里并发执行所有任务
        })
    tasks = []
}
function main() {
    while(true){
        produce()         // 发出交易指令
        worker()        // 并发执行
        Sleep(1000)
    }
}

ऐसा लगता है कि हमने बहुत सारे चरणों से गुज़रने के बाद केवल एक सरल फ़ंक्शन को लागू किया है, लेकिन वास्तव में, कोड की जटिलता को बहुत सरल बना दिया गया है। हमें केवल इस बात की परवाह करने की ज़रूरत है कि प्रोग्राम को कौन से कार्य उत्पन्न करने की आवश्यकता है, और कार्यकर्ता( ) प्रोग्राम स्वचालित रूप से उन्हें एक साथ निष्पादित करेगा और संबंधित परिणाम लौटाएगा। लचीलेपन में काफी सुधार हुआ है।

Chart फ़ंक्शन को रेखांकित करें

प्रारंभिक ट्यूटोरियल चित्रों का परिचय चित्रों की एक अनुशंसित श्रेणी है, जो ज्यादातर मामलों में आवश्यकताओं को पूरा कर सकती है। यदि आपको और अधिक अनुकूलन की आवश्यकता है, तो आप सीधे चार्ट ऑब्जेक्ट पर काम कर सकते हैं।

Chart({…})आंतरिक पैरामीटर HighStock और HighCharts ऑब्जेक्ट हैं, केवल एक अतिरिक्त पैरामीटर जोड़ा गया है__isStockHighStock. HighStock समय अनुक्रम के चार्ट पर अधिक ध्यान केंद्रित करता है, इसलिए यह अधिक आम है। FMZ मूल रूप से HighCharts और HighStock के मूल मॉड्यूल का समर्थन करता है, लेकिन अतिरिक्त मॉड्यूल का समर्थन नहीं करता है।

HighCharts के विशिष्ट उदाहरणः https://www.highcharts.com/demo; HighStock उदाहरणः https://www.highcharts.com/stock/demo 。 इन उदाहरणों के कोड को FMZ पर आसानी से स्थानांतरित किया जा सकता है。

हम add([series index ((जैसे 0), डेटा]) निर्दिष्ट अनुक्रमणिका की श्रृंखला में डेटा जोड़ें, reset () कॉल करें खाली आरेख डेटा, reset एक संख्यात्मक पैरामीटर के साथ हो सकता है, आरक्षित आरेखों की संख्या निर्दिष्ट करें। कई आरेखों को प्रदर्शित करने का समर्थन करता है, कॉन्फ़िगर करने के लिए बस सरणी पैरामीटर पास करना आवश्यक है जैसेः var chart = Chart ()[{…}, {…}, {…}]), उदाहरण के लिए ग्राफ एक में दो श्रृंखलाएं हैं, ग्राफ दो में एक श्रृंखला है, और ग्राफ तीन में एक श्रृंखला है, तो add में 0 और 1 अनुक्रम आईडी को निर्दिष्ट करने से ग्राफ 1 के दो अनुक्रमों के डेटा को अपडेट किया जाता है, जबकि add में अनुक्रम आईडी 2 को ग्राफ 2 की पहली श्रृंखला के डेटा को निर्दिष्ट किया जाता है, और अनुक्रम 3 को ग्राफ 3 की पहली श्रृंखला के डेटा को निर्दिष्ट किया जाता है।

एक विशिष्ट उदाहरण:

var chart = { // 这个 chart 在JS 语言中 是对象, 在使用Chart 函数之前我们需要声明一个配置图表的对象变量chart。
    __isStock: true,                                    // 标记是否为一般图表,有兴趣的可以改成 false 运行看看。
    tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},    // 缩放工具
    title : { text : '差价分析图'},                       // 标题
    rangeSelector: {                                    // 选择范围
        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'},                         // 坐标轴横轴 即:x轴, 当前设置的类型是 :时间
    yAxis : {                                           // 坐标轴纵轴 即:y轴, 默认数值随数据大小调整。
        title: {text: '差价'},                           // 标题
        opposite: false,                                // 是否启用右边纵轴
    },
    series : [                                          // 数据系列,该属性保存的是 各个 数据系列(线, K线图, 标签等..)
        {name : "line1", id : "线1,buy1Price", data : []},  // 索引为0, data 数组内存放的是该索引系列的 数据
        {name : "line2", id : "线2,lastPrice", dashStyle : 'shortdash', data : []}, // 索引为1,设置了dashStyle : 'shortdash' 即:设置 虚线。
    ]
};
function main(){
    var ObjChart = Chart(chart);  // 调用 Chart 函数,初始化 图表。
    ObjChart.reset();             // 清空
    while(true){
        var nowTime = new Date().getTime();   // 获取本次轮询的 时间戳,  即一个 毫秒 的时间戳。用来确定写入到图表的X轴的位置。
        var ticker = _C(exchange.GetTicker);  // 获取行情数据
        var buy1Price = ticker.Buy;           // 从行情数据的返回值取得 买一价
        var lastPrice = ticker.Last + 1;      // 取得最后成交价,为了2条线不重合在一起 ,我们加1
        ObjChart.add([0, [nowTime, buy1Price]]); // 用时间戳作为X值, 买一价 作为Y值 传入 索引0 的数据序列。
        ObjChart.add([1, [nowTime, lastPrice]]); // 同上。
        Sleep(2000);
    }
}

एक उदाहरण जो चार्ट लेआउट का उपयोग करता हैः https://www.fmz.com/strategy/136056

आगे की प्रतिक्रिया

पायथन स्थानीय प्रतिक्रिया

विशिष्ट ओपन सोर्स पताः https://github.com/fmzquant/backtest_python

स्थापित करें

कमांड लाइन में निम्न कमांड दर्ज करेंः

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

सरल उदाहरण

फीडबैक पैरामीटर को नीति कोड की शुरुआत में एक टिप्पणी के रूप में सेट किया गया है, विशेष रूप से एफएमजेड वेबसाइट पर नीति संपादन इंटरफ़ेस में फीडबैक सेटिंग्स को सहेजें।

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

बैकटेस्टिंग

चूंकि एक पूर्ण रणनीति के लिए एक मृत चक्र की आवश्यकता होती है, इसलिए एक त्रुटि सहनशीलता की आवश्यकता होती है, क्योंकि ईओएफ असामान्यता को रीट्रेसिंग के अंत में समाप्त करने के लिए फेंक दिया जाएगा।

# !/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__

# ------------------------------ 策略部分开始 --------------------------

print exchange.GetAccount()     # 调用一些接口,打印其返回值。
print exchange.GetTicker()

def adjustFloat(v):             # 策略中自定义的函数
    v = math.floor(v * 1000)
    return v / 1000

def onTick():
    Log("onTick")
    # 具体的策略代码


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

# ------------------------------ 策略部分结束 --------------------------

try:
    main()                     # 回测结束时会 raise EOFError() 抛出异常,来停止回测的循环。所以要对这个异常处理,在检测到抛出的异常后调用 task.Join() 打印回测结果。
except:
    print task.Join()         

कस्टम फीडबैक डेटा

exchange.SetData(arr), रिप्लेसमेंट डेटा स्रोतों को स्विच करें, कस्टम K-लाइन डेटा का उपयोग करें. Arr पैरामीटर, एक K-लाइन स्तंभ डेटा के लिए एक तत्व है ((यानीः K-लाइन डेटा सरणी, जो केवल जावास्क्रिप्ट रिप्लेसमेंट का समर्थन करता है।

arr सरणी में, प्रत्येक तत्व का डेटा प्रारूप हैः

[
    1530460800,    // time     时间戳
    2841.5795,     // open     开盘价
    2845.6801,     // high     最高价
    2756.815,      // low      最低价
    2775.557,      // close    收盘价
    137035034      // volume   成交量
]

डेटा स्रोतों को टेम्पलेट श्रेणी में आयात किया जा सकता है.

function init() {                                                          // 模板中的 init 初始化函数会在加载模板时,首先执行,确保 exchange.SetData(arr) 函数先执行,初始化,设置数据给回测系统。
    var arr = [                                                            // 回测的时候需要使用的K线数据
        [1530460800,2841.5795,2845.6801,2756.815,2775.557,137035034],      // 时间最早的一根 K线柱 数据
        ... ,                                                              // K线数据太长,用 ... 表示,数据此处省略。
        [1542556800,2681.8988,2703.5116,2674.1781,2703.5116,231662827]     // 时间最近的一根 K线柱 数据
    ]
    exchange.SetData(arr)                                                  // 导入上述 自定义的数据
    Log("导入数据成功")
}

ध्यान देंः जब भी आरंभिकरण किया जाता है, तो पहले कस्टम डेटा आयात किया जाता है (यानी, exchange.SetData फ़ंक्शन सेट डेटा को कॉल करें), कस्टम K-लाइन डेटा चक्र को वापस मापने वाले पृष्ठ पर सेट किए गए अंडरलाइन K-लाइन चक्र के साथ मेल खाना चाहिए, अर्थात्ः कस्टम K-लाइन डेटा, एक K-लाइन समय 1 मिनट है, तो वापस मापने में सेट अंडरलाइन K-लाइन चक्र भी 1 मिनट के लिए सेट किया गया है।

FMZ का उपयोग करने वाले एक्सचेंजों का समर्थन नहीं

यदि असमर्थित एक्सचेंज और समर्थित एक्सचेंज का एपीआई बिल्कुल समान है, केवल आधार पता अलग है, तो यह आधार पता स्विच करके समर्थित किया जा सकता है। एक्सचेंज को जोड़ने के लिए समर्थित एक्सचेंज का चयन करें, लेकिन एपीआई-केआई असमर्थित एक्सचेंज को भरें, नीति में आईओ के साथ आधार पता स्विच करें, जैसेः

exchange.IO("base", "http://api.huobi.pro") 
//http://api.huobi.pro为为支持交易所API基地址,注意不用添加/api/v3之类的,会自动补全

सभी एक्सचेंजों ने एफएमजेड का समर्थन नहीं किया है, लेकिन मंच सामान्य समझौते तक पहुंच प्रदान करता है।

  • यह एक वेब सेवा बनाने के लिए एक्सचेंजों के लिए अपने स्वयं के कोड लिखता है
  • एफएमजेड प्लेटफॉर्म पर एक्सचेंज जोड़ें, नेटवर्क सेवाओं के पते और पोर्ट निर्दिष्ट करें।
  • जब ट्रस्टी जीपीओ के एक्सचेंजों के लिए एक वास्तविक प्लेटफ़ॉर्म चलाता है, तो नीति में एपीआई एक्सेस जीपीओ को भेजा जाता है।
  • सामान्य समझौता अनुरोध पर एक्सचेंज का दौरा करता है और परिणाम को ट्रस्टी को लौटाता है।

सरल शब्दों में कहें तो, एक जनरल प्रोटोकॉल एक मध्यस्थ के बराबर होता है, जो संबंधित मानकों के अनुसार मेजबान के अनुरोध का प्रतिनिधित्व करता है और डेटा वापस करता है। जनरल प्रोटोकॉल का कोड स्वयं को पूरा करने की आवश्यकता होती है, जनरल प्रोटोकॉल को लिखना वास्तव में यह दर्शाता है कि आप अलग-अलग एक्सचेंजों तक पहुंच सकते हैं और रणनीति को पूरा कर सकते हैं। एफएमजेड अधिकारियों ने कभी-कभी एक्सचेंजों के लिए जनरल प्रोटोकॉल का एक संस्करण जारी किया है। जनरल प्रोटोकॉल को पायथन के साथ भी पूरा किया जा सकता है, जो तब एक सामान्य डिस्क के रूप में वास्तविक मेजबान पर चल सकता है।

इस समझौते के बारे मेंः https://www.fmz.com/bbs-topic/1052 सामान्य प्रोटोकॉल लिखने के लिए पाइथन का उदाहरणः https://www.fmz.com/strategy/101399

अपना खुद का क्वांटिटेटिव प्लेटफॉर्म बनाएं

एफएमजेड वेबसाइट भी एपीआई-आधारित है, आप अपनी एफएमजेड वेबसाइट एपीआई-की को लागू करने के लिए आवेदन कर सकते हैं जैसे कि वास्तविक डिस्क बनाने, पुनः आरंभ करने, हटाने, वास्तविक डिस्क सूची प्राप्त करने, वास्तविक डिस्क लॉग प्राप्त करने जैसे विभिन्न कार्य।

FMZ प्लेटफ़ॉर्म की मजबूत स्केलेबिलिटी के कारण, आप अपने स्वयं के क्वांटिटेशन प्लेटफ़ॉर्म को एक्सटेंशन एपीआई के आधार पर बना सकते हैं, जिससे उपयोगकर्ता आपके प्लेटफ़ॉर्म पर रीयल-टाइम चल सकते हैं।

एफएमजेड में भागीदार बनें

क्लाउड क्लाउड को बढ़ावा देना

डिजिटल मुद्रा व्यापार बाजार इसकी विशिष्टता के कारण मात्रा व्यापारियों के लिए अधिक से अधिक ध्�