이전 기사에서는 쌍 거래의 원칙과 백테스팅을 소개했습니다.https://www.fmz.com/bbs-topic/10459. 여기에 FMZ 플랫폼을 기반으로 한 실용적인 소스 코드가 있습니다. 전략은 간단하고 명확하며 초보자 학습에 적합합니다. FMZ 플랫폼은 최근 다중 거래 쌍 전략에 더 친화적 인 일부 API를 업그레이드했습니다. 이 기사는이 전략의 자바스크립트 소스 코드를 자세히 소개합니다. 전략 코드가 단지 백 줄이지만 완전한 전략에 필요한 모든 측면을 포함합니다. 특정 API는 API 문서에서 볼 수 있습니다. 매우 상세합니다. 전략 공개 주소:https://www.fmz.com/strategy/456143직접 복사할 수 있습니다.
FMZ 플랫폼에 익숙하지 않다면 이 튜토리얼을 읽어보시기 바랍니다.https://www.fmz.com/bbs-topic/4145이 책에서는 플랫폼의 기본적인 기능과 로봇을 처음부터 어떻게 배치할 수 있는지에 대해 자세히 소개합니다.
다음은 간단한 전략 프레임워크입니다. 주요 기능은 입문점입니다. 무한 루프는 전략이 지속적으로 실행되도록 보장하며, 액세스 주파수가 교환 한계를 초과하는 것을 방지하기 위해 작은 수면 시간이 추가됩니다.
function main(){
while(true){
//strategy content
Sleep(Interval * 1000) //Sleep
}
}
로봇은 오류, 매개 변수 업데이트, 전략 업데이트 등 다양한 이유로 반복적으로 다시 시작되며 다음 시작에 일부 데이터가 저장되어야합니다. 수익을 계산하기 위해 초기 자본을 저장하는 방법에 대한 예시입니다. _G() 함수는 다양한 데이터를 저장할 수 있습니다. _G(key, value) 는 값의 값을 저장하고 키가 문자열인 _G(key로 호출 할 수 있습니다.
let init_eq = 0 //defining initial equity
if(!_G('init_eq')){ //If there is no storage, _G('init_eq') returns null.
init_eq = total_eq
_G('init_eq', total_eq) //Since there is no storage, the initial equity is the current equity and is stored here
}else{
init_eq = _G('init_eq') //If stored, read the value of the initial equity
}
API를 통해 포지션 및 시장 조건과 같은 데이터를 얻는 경우 여러 가지 이유로 오류가 발생할 수 있습니다. 데이터를 직접 호출하면 오류로 인해 전략이 중단 될 수 있으므로 오류 용인 메커니즘이 필요합니다. _C() 함수는 올바른 데이터가 반환 될 때까지 오류가 발생하면 자동으로 다시 시도합니다. 또는 반환 후 데이터가 있는지 확인하십시오.
let pos = _C(exchange.GetPosition, pair)
let ticker_A = exchange.GetTicker(pair_a)
let ticker_B = exchange.GetTicker(pair_b)
if(!ticker_A || !ticker_B){
continue //If the data is not available, exit the loop.
}
GetPosition, GetTicker, GetRecords와 같은 기능은 교환 결합 거래 쌍을 설정 할 필요없이 해당 데이터를 얻기 위해 거래 쌍 매개 변수를 추가 할 수 있으며, 이는 여러 거래 쌍 전략의 호환성을 크게 촉진합니다. 특정 업그레이드 콘텐츠를 위해 기사를 참조하십시오:https://www.fmz.com/bbs-topic/10456물론, 최신 도커를 지원하는 것이 필요합니다. 만약 도커가 너무 오래되면 업그레이드해야 합니다.
아직 이해가 안된다면 FMZ의 API 문서, 디버깅 도구, 그리고 시장에서 일반적으로 사용되는 AI 대화 도구를 사용하여 질문을 해결할 수 있습니다.
function GetPosition(pair){
let pos = _C(exchange.GetPosition, pair)
if(pos.length == 0){ //Returns null to indicate no position
return {amount:0, price:0, profit:0}
}else if(pos.length > 1){ //The strategy should be set to unidirectional position mode
throw 'Bidirectional positions are not supported'
}else{ //For convenience, long positions are positive and short positions are negative
return {amount:pos[0].Type == 0 ? pos[0].Amount : -pos[0].Amount, price:pos[0].Price, profit:pos[0].Profit}
}
}
function GetRatio(){
let kline_A = exchange.GetRecords(Pair_A+"_"+Quote+".swap", 60*60, N) //Hourly K-line
let kline_B = exchange.GetRecords(Pair_B+"_"+Quote+".swap", 60*60, N)
let total = 0
for(let i= Math.min(kline_A.length,kline_B.length)-1; i >= 0; i--){ //Calculate in reverse to avoid the K-line being too short.
total += kline_A[i].Close / kline_B[i].Close
}
return total / Math.min(kline_A.length,kline_B.length)
}
function GetAccount(){
let account = _C(exchange.GetAccount)
let total_eq = 0
if(exchange.GetName == 'Futures_OKCoin'){ //Since the API here is not compatible, only OKX Futures Exchange obtains the total equity currently.
total_eq = account.Info.data[0].totalEq //The equity information of other exchanges is also included. You can look for it yourself in the exchange API documentation.
}else{
total_eq = account.Balance //Temporary use of available balances on other exchanges will cause errors in calculating returns, but will not affect the use of strategies.
}
let init_eq = 0
if(!_G('init_eq')){
init_eq = total_eq
_G('init_eq', total_eq)
}else{
init_eq = _G('init_eq')
}
LogProfit(total_eq - init_eq)
return total_eq
}
function main(){
var precision = exchange.GetMarkets() //Get the precision here
var last_get_ratio_time = Date.now()
var ratio = GetRatio()
var total_eq = GetAccount()
while(true){
let start_loop_time = Date.now()
if(Date.now() - last_get_ratio_time > 10*60*1000){ //Update the average price and account information every 10 minutes
ratio = GetRatio()
total_eq = GetAccount()
last_get_ratio_time = Date.now()
}
let pair_a = Pair_A+"_"+Quote+".swap" //The trading pair is set as BTC_USDT.swap
let pair_b = Pair_B+"_"+Quote+".swap"
let CtVal_a = "CtVal" in precision[pair_a] ? precision[pair_a].CtVal : 1 //Some exchanges use sheets to represent quantity, such as one sheet represents 0.01 coin, so you need to convert.
let CtVal_b = "CtVal" in precision[pair_b] ? precision[pair_b].CtVal : 1 //No need to include this field
let position_A = GetPosition(pair_a)
let position_B = GetPosition(pair_b)
let ticker_A = exchange.GetTicker(pair_a)
let ticker_B = exchange.GetTicker(pair_b)
if(!ticker_A || !ticker_B){ //If the returned data is abnormal, jump out of this loop
continue
}
let diff = (ticker_A.Last / ticker_B.Last - ratio) / ratio //Calculate the ratio of deviation
let aim_value = - Trade_Value * diff / Pct //Target holding position
let id_A = null
let id_B = null
//The following is the specific logic of opening a position
if( -aim_value + position_A.amount*CtVal_a*ticker_A.Last > Trade_Value && position_A.amount*CtVal_a*ticker_A.Last > -Max_Value){
id_A = exchange.CreateOrder(pair_a, "sell", ticker_A.Buy, _N(Ice_Value / (ticker_A.Buy * CtVal_a), precision[pair_a].AmountPrecision))
}
if( -aim_value - position_B.amount*CtVal_b*ticker_B.Last > Trade_Value && position_B.amount*CtVal_b*ticker_B.Last < Max_Value){
id_B = exchange.CreateOrder(pair_b, "buy", ticker_B.Sell, _N(Ice_Value / (ticker_B.Sell * CtVal_b), precision[pair_b].AmountPrecision))
}
if( aim_value - position_A.amount*CtVal_a*ticker_A.Last > Trade_Value && position_A.amount*CtVal_a*ticker_A.Last < Max_Value){
id_A = exchange.CreateOrder(pair_a, "buy", ticker_A.Sell, _N(Ice_Value / (ticker_A.Sell * CtVal_a), precision[pair_a].AmountPrecision))
}
if( aim_value + position_B.amount*CtVal_b*ticker_B.Last > Trade_Value && position_B.amount*CtVal_b*ticker_B.Last > -Max_Value){
id_B = exchange.CreateOrder(pair_b, "sell", ticker_B.Buy, _N(Ice_Value / (ticker_B.Buy * CtVal_b), precision[pair_b].AmountPrecision))
}
if(id_A){
exchange.CancelOrder(id_A) //Cancel directly here
}
if(id_B){
exchange.CancelOrder(id_B)
}
let table = {
type: "table",
title: "trading Information",
cols: ["initial equity", "current equity", Pair_A+"position", Pair_B+"position", Pair_A+"holding price", Pair_B+"holding price", Pair_A+"profits", Pair_B+"profits", Pair_A+"price", Pair_B+"price", "current price comparison", "average price comparison", "deviation from average price", "loop delay"],
rows: [[_N(_G('init_eq'),2), _N(total_eq,2), _N(position_A.amount*CtVal_a*ticker_A.Last, 1), _N(position_B.amount*CtVal_b*ticker_B.Last,1),
_N(position_A.price, precision[pair_a].PircePrecision), _N(position_B.price, precision[pair_b].PircePrecision),
_N(position_A.profit, 1), _N(position_B.profit, 1), ticker_A.Last, ticker_B.Last,
_N(ticker_A.Last / ticker_B.Last,6), _N(ratio, 6), _N(diff, 4), (Date.now() - start_loop_time)+"ms"
]]
}
LogStatus("`" + JSON.stringify(table) + "`") //This function will display a table containing the above information on the robot page.
Sleep(Interval * 1000) //Sleep time in ms
}
}