[TOC] 이 강좌를 배우기 전에 먼저 배워야 합니다.FMZ 발명가 양적 플랫폼 사용 입문그리고FMZ 양적 플랫폼 전략에 대한 초급 튜토리얼그리고 프로그래밍 언어를 능숙하게 사용한다.초급 튜토리얼은 가장 많이 사용되는 함수들에 대해 다루고 있지만, 이 튜토리얼에서는 다루지 않는 많은 함수와 기능들이 있습니다. 플랫폼 API 문서를 직접 살펴보면 알 수 있습니다.이 튜토리얼을 마친 후에는 좀 더 자유롭고, 맞춤화된 전략을 쓸 수 있을 것입니다. FMZ 플랫폼은 단지 도구일 뿐입니다.
FMZ 플랫폼은 모든 지원하는 거래소를 포괄하고, 단일화를 유지하기 위해 단일 거래소 API 지원이 완전하지 않습니다. K 라인 수 또는 시작 시간을 입력하면 K 라인 수 또는 시작 시간을 전송할 수 있습니다. FMZ 플랫폼은 고정되어 있으며, 일부 플랫폼은 대량 주문을 지원하고, FMZ는 지원하지 않습니다.공개 인터페이스는 (사용 가능한 경우)HttpQuery[계좌 정보에 접근하기 위해]IO。특정 입력 매개 변수는 해당 거래소 API 문서를 참조하십시오. 지난 튜토리얼에서 설명했습니다.Info이 필드는 원본 정보를 반환했지만 여전히 인터페이스를 지원하지 않는 문제를 해결할 수 없습니다.
마지막 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)
}
Js를 사용할 수 있는 공개 인터페이스에 액세스합니다.HttpQuery파이썬은 자체적으로 관련 패키지를 사용할 수 있습니다.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에서 request을 사용하는 예시
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를 통해 구현한다.
https://www.bitmex.com/api/explorer/。https://www.bitmex.com/api/v1/order, 방법POSTFMZ는 이미 내부적으로 root 주소를 지정하고 있기 때문에 “/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"}))
더 많은 IO 예시: https://www.fmz.com/bbs-topic/3683
기본적으로 모든 디지털 화폐 거래소는 websocket 발송을 지원하고 일부 거래소는 websocket 업데이트 계정 정보를 지원한다. rest API에 비해 websocket은 일반적으로 지연이 낮고, 주파수가 높으며, 플랫폼 rest API 주파수 제한을 받지 않는다. 단점은 중단 문제가 있고, 처리 직관적이지 않다.
이 문서는 주로 FMZ 발명자의 양적 플랫폼에 대해 소개할 것이며, JavaScript 언어를 사용하여 플랫폼의 패키지된 Dial 함수를 사용하여 연결할 것이며, 설명과 매개 변수는 문서에 나와 있으며, Dial 검색, 다양한 기능을 구현하기 위해 Dial 함수가 여러 번 업데이트되었습니다. 이 문서는 이것을 다루며, wss 기반의 이벤트 드라이브 전략을 소개하고, 다중 거래소 연결 문제를 소개할 것입니다.
일반적으로 직접 연결할 수 있는데, 예를 들어, 화폐 보안 티커를 전송할 수 있습니다.
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
복귀 데이터의 압축 형식은 연결에서 지정해야 하며, compress은 압축 형식을 지정하고, modde는 복귀 데이터를 전송하는 데 압축이 필요한 것을 나타냅니다. 연결 OKEX처럼:
var client = Dial("wss://real.okex.com:10441/websocket?compress=true|compress=gzip_raw&mode=recv")
Dial 함수는 재연결을 지원하며, 기본 Go 언어에서 완료되며, 검출된 연결 끊기 회담 재연결, 요청 데이터 내용이 이미 url에 있는 경우, 예를 들어 방금 동전, 편리하고, 권장한다. 메시지를 보내야 하는 경우, 자체적으로 재연결 메커니즘을 유지할 수 있다.
var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr|reconnect=true")
일부 거래소들의 요청은 url에 있고, 일부 거래소들은 Coinbase와 같은 채널에 직접 가입해야 합니다.
client = Dial("wss://ws-feed.pro.coinbase.com", 60)
client.write('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker","heartbeat"]}')
일반적으로 웹소켓을 사용하지만, 주문이나 계정 추진을 얻을 수도 있다. 이러한 암호화 데이터의 추진은 때때로 큰 지연이 있을 수 있으므로 주의해야 한다. 암호화 방법이 더 복잡하기 때문에 몇 가지 예제를 참고로 제시한다. 참고로 모두 AccessKey만 필요하며, 정책 파라미트로 설정할 수 있다. SecretKey가 필요하면, exchange.HMAC () 함수의 암시 호출을 사용하여 보안을 보장한다.
//火币期货推送例子
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)
}
}
이 코드들은 일반적으로 죽은 순환에서 계속 읽을 수 있습니다.
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数据
}
}
wss 데이터 푸싱 속도는 매우 빠르며, Go의 바닥은 모든 데이터를 큐에 저장하여, read라는 프로그램이 호출될 때, 다시 순차적으로 반환한다. 그리고 리스크의 주문과 같은 작업은 지연을 가져오며, 데이터의 축적을 초래할 수 있다. 거래 푸싱, 계정 푸싱, 깊이 삽입값 푸싱 등의 정보에 대해서는 역사 데이터가 필요하며, 실재 데이터에 대해서는 대부분의 경우 최신의 데이터에만 관심을 가지며, 역사 데이터를 신경 쓰지 않는다.
read()만약 변수를 추가하지 않으면, 가장 오래된 데이터를 반환하고, 데이터가 없으면 반환을 차단한다. 최신 데이터를 원한다면,client.read(-2)즉시 최신 데이터를 반환하지만, 더 이상 데이터가 없을 때 null을 반환하여 다시 참조해야 합니다.
캐시된 오래된 데이터를 어떻게 처리하는지, 그리고 데이터가 없을 때 막히는지에 따라, read에는 다른 파라미터가 있다. 구체적으로 아래 그림처럼, 복잡하게 보이지만 프로그램을 더 유연하게 한다.

이러한 경우의 절차는 간단하게 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
}
}
이 부분 처리에는 다소 번거로움이 있는데, 푸싱 데이터가 중단될 수도 있고, 혹은 푸싱 지연이 매우 높기 때문에, heartbeat를 수신할 수 있더라도 데이터가 계속 푸싱되고 있다는 것을 의미하지 않는다. 하나의 이벤트 간격을 설정할 수 있으며, 간격이 넘어서도 업데이트를 받지 못하면 다시 연결하고, 시간이 지나면 rest과 반환된 결과와 비교하여 데이터가 정확하는지 확인하는 것이 좋다. 동전 안의 이 특별한 경우에, 직접 자동 재 연결을 설정할 수 있다.
푸시 데이터를 사용했기 때문에, 프로그램은 자연스레 이벤트 드라이브로 작성해야 합니다. 푸시 데이터가 자주 발생하여 너무 많은 요청으로 인해 차단되지 않도록 주의하십시오. 일반적으로 다음과 같이 쓸 수 있습니다.
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)
}
}
각 거래소의 웹소켓의 연결 방식, 데이터 전송 방식, 구독할 수 있는 내용, 데이터 포맷은 서로 다르기 때문에 플랫폼은 패키징이 되지 않고 다이얼 함수를 사용하여 자동으로 연결해야 합니다. 이 글은 기본적으로 몇 가지 기본적인 주의 사항을 다루고 있으며, 질문이 있으면 문의하시기 바랍니다.
PS. 일부 거래소에서는 websocket 행렬이 제공되지 않지만, 실제로 웹사이트에 로그인할 때 websocket 푸싱 기능이 사용되고 있는 것을 볼 수 있습니다. 검색을 하면 서브스크립트 포맷과 리턴 포맷이 발견됩니다.
JavaScript는 Go 함수를 통해 병렬을 구현할 수 있으며, Python은 그에 따른 다중 스레드 라이브러리를 사용할 수 있다.
정량화 전략을 구현할 때, 많은 경우에, 동시 실행은 지연 제고의 효율성을 감소시킬 수 있다. 예를 들어, 헤지 전략 실디스크를 위해 두 동전의 깊이를 얻어야 하며, 순서대로 실행되는 코드는 다음과 같다:
var depthA = exchanges[0].GetDepth()
var depthB = exchanges[1].GetDepth()
REST API를 요청하는 데 지연이 있습니다. 100ms라고 가정합니다. 그러면 깊이를 두 번 얻는 데 걸리는 시간은 실제로 다릅니다. 더 많은 액세스가 필요한 경우 지연 문제가 더 두드러져 전략 실행에 영향을 미칩니다.
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() //调用wait方法等待返回异步获取depth结果
var depthB = b.wait()
대부분의 간단한 경우에는 이런 방식으로 정책을 작성하는 데 아무런 문제가 없습니다. 하지만 이 과정은 전략 루프를 돌 때마다 반복해야 하며, 중간 변수 a와 b는 실제로는 일시적인 보조 도구일 뿐입니다. 동시 작업이 많으면 a와 depthA, b와 depthB 사이의 대응 관계를 기록해야 합니다. 동시 작업이 불확실하면 상황은 더 복잡해집니다. 따라서 우리는 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}] //加入我们需要获取两个交易所的深度和账户,跟多的信息也可以放入,如订单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 인덱스 (예: 0), 데이터]) 는 지정된 인덱스의 series에 데이터를 추가하고, reset () 를 호출한다. reset () 는 비어있는 차트 데이터를 가져오고, reset () 는 디지털 변수를 가져다가, 보존된 항목을 지정한다. 여러 차트를 표시하는 것이 지원되며, 구성 시에는 배열 변수를 입력하면 됩니다. var chart = Chart ()[{…}, {…}, {…}]), 예를 들어 그림 1은 두 개의 시리즈가 있고, 그림 2은 하나의 시리즈가 있고, 그림 3은 하나의 시리즈가 있는데, add을 할 때 0과 1의 서열 ID를 지정하면 그림 1의 두 개의 서열의 데이터를 업데이트하고, add을 할 때 서열 ID를 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
간단한 예제
리포트 파라미터는 정책 코드의 시작에서 코멘트 형태로 설정된다. 구체적으로 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__
# ------------------------------ 策略部分开始 --------------------------
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선 데이터를 사용한다. arguments arr, K선 기둥 데이터의 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분으로 설정된다.
지원되지 않는 거래소와 지원된 거래소의 API가 완전히 동일하지만 기지 주소가 다르다면 기지 주소를 전환하는 방식으로 지원할 수 있다. 구체적으로 거래소를 추가할 때 지원된 거래소를 선택하지만 API-KEY를 채우면 지원되지 않는 거래소는 전략에서 IO로 기지 주소를 전환한다.
exchange.IO("base", "http://api.huobi.pro")
//http://api.huobi.pro为为支持交易所API基地址,注意不用添加/api/v3之类的,会自动补全
모든 거래소가 FMZ를 지원하는 것은 아니지만, 이 플랫폼은 일반적인 협약에 대한 접근을 제공합니다.
간단히 말해서, GMAT은 해당 표준에 따라 호스트의 요청을 대리하고 데이터를 반환하는 중개자와 같습니다. GMAT의 코드는 스스로 작성해야하며, GMAT를 작성하는 것은 실제로 당신이 거래소에 개별적으로 액세스하여 전략을 완료 할 수 있음을 의미합니다. FMZ는 때때로 거래소의 GMAT의 exe 버전을 공식적으로 릴리스합니다. GMAT는 파이썬을 사용하여 수행 할 수 있으며, 이는 일반 디스크처럼 실제 호스트에서 작동 할 수 있습니다.
이 협약에 대한 자세한 내용은 https://www.fmz.com/bbs-topic/1052에서 확인할 수 있습니다. Python에서 쓰기 위한 일반적인 프로토콜의 예는:https://www.fmz.com/strategy/101399
거래소와 마찬가지로 API를 통해 모든 작업을 수행 할 수 있으며, FMZ 웹 사이트도 API 기반입니다. FMZ 웹 사이트 API-KEY 구현을 신청할 수 있습니다.
FMZ 플랫폼의 강력한 확장성으로 인해 확장된 API를 기반으로 사용자 자신의 양적 플랫폼을 만들 수 있습니다.
디지털 통화 거래 시장은 그 특수성으로 인해 양자 거래자의 관심이 증가하고 있으며, 실제로 프로그램 거래는 이미 디지털 통화의 주류이며, 헤퍼링 시장과 같은 전략은 항상 시장에서 활동하고 있습니다. 프로그래밍 기반이 약한 초보자가이 분야에 진입하기를 원하는 경우, 수많은 거래소와 다변화 된 API에 직면하여 어려움을 겪습니다. 발명자 (FMZ) 양자 플랫폼 (원래의 BotVs, www.fmz.com) 은 현재 가장 큰 디지털 통화 커뮤니티 양자 플랫폼이며, 4 년 이상 수천 명의 초보자가 양자 거래의 길을 걸도록 도와 왔습니다. 이 과정은 20 달러에 초보자를 대상으로합니다.
홍보디지털 화폐의 양적 거래에 대한 NetEase 클라우드 강의ᄂ) NetEase Cloud 수업에 접속하여, 여러분의 강의 링크를 공유하세요 (이 링크는 고유한 courseId를 가지고 있습니다), 다른 사람들이 이 링크를 통해 등록하고, 프로그램을 구매하면, 여러분은 50%의 총 10원 분할을 받게 됩니다. ᄂ) NetEase Cloud 수업의 뷰티 수업 홍보에 관심하세요. ᄂ)微信 공공 번호를 제공하세요.
소비자는 홍보 링크를 클릭하고, 반년 이내에 등록 충전, 우리 부는 유효 주문의 유효 금액에 따라 수수료를 반환한다. 수수료는 분량 형태로 홍보자의 계정에 반환된다. 사용자는 10:1 비율로 발명자의 양적 거래 플랫폼 계정 잔액을 교환할 수 있으며, 또한 나중에 분량으로 발명자의 양적 상품을 교환할 수 있다. 구체적인 활동 링크: https://www.fmz.com/bbs-topic/3828
전체 FMZ 웹사이트를 기업 또는 팀의 전용 서버에 배포하여 완전한 제어 및 기능 맞춤화를 구현할 수 있다. FMZ 웹사이트는 약 10만 명의 사용자에 의해 사용 및 테스트를 거쳐 매우 높은 가용성 및 보안을 달성하고 있으며, 양화 팀 및 기업의 시간 비용을 절약할 수 있다. 기업 버전은 중소형 양화 거래 팀, 상품 선물 서비스 업체 등을 대상으로 하며, 구체적인 견적을 관리자에게 문의하십시오.
거래소에게 시장 유동성과 자금 관리를 제공하는 전문 시스템은 시장에서 가장 완벽한 거래 시스템이며 많은 거래소와 팀이 사용합니다.
발명자의 기술 거래 시스템은 메모리 기술, 주문 처리 속도 최대 200 만 / 초, 주문 처리 아무런 지연과 카튼이 발생하지 않도록 보장 할 수 있습니다. 동시에 온라인 사용자 수 200 만 이상의 거래소의 원활한 안정적인 운영을 유지할 수 있습니다. 다층, 다중 클러스터의 시스템 구조는 시스템의 안전성, 안정성, 확장성을 보장합니다. 기능 배포, 버전 업데이트는 정지없이 진행되며, 최종 사용자의 운영 경험을 최대한 보장합니다. 현재 wex.app 모의 거래소에서이 시스템을 경험할 수 있습니다.