
이 글의 목적은 거래 전략 개발에 대한 몇 가지 경험과 팁을 공유하는 것입니다. 이를 통해 독자들은 거래 전략 개발에 대한 경험을 빠르게 이해할 수 있을 것입니다. 전략 설계에서 이와 유사한 세부적인 문제에 부딪히면, 합리적인 해결책을 즉시 생각해 낼 수 있습니다. Inventor Quantitative Trading Platform은 설명, 테스트 및 연습을 위한 플랫폼으로 사용됩니다. 정책 프로그래밍 언어: JavaScript 거래 시장: 블록체인 자산 시장(BTC, ETH 등)
일반적으로 전략 로직에 따라 다음과 같은 다양한 인터페이스를 사용하여 시장 데이터를 얻을 수 있습니다. 전략의 거래 로직은 일반적으로 시장 데이터에 의해 구동되기 때문입니다. (물론 시장을 살펴보지 않는 전략도 있습니다.) (예: 고정 투자 전략)
GetTicker: 실시간 틱 정보를 받아보세요. 일반적으로 최신 가격, 매수 가격, 매도 가격을 빠르게 얻는 데 사용됩니다.
GetDepth: 주문장 깊이 견적을 받습니다. 이는 일반적으로 각 레벨의 가격과 주문 규모를 얻는 데 사용됩니다. 헤지 전략, 시장 조작 전략 등에 사용됩니다.
GetTrade: 시장의 최신 거래 기록을 받아보세요. 일반적으로 단기간의 시장 동향을 분석하고, 시장의 미시적 변화를 분석하는 데 사용됩니다. 일반적으로 고빈도 전략과 알고리즘 전략에 사용됩니다.
GetRecords: 시장의 K-라인 데이터를 받아보세요. 종종 추세 추종 전략에 사용됩니다. 지표를 계산하는 데 사용됩니다.
전략을 설계할 때 초보자는 일반적으로 다양한 오류 상황을 무시하고 전략의 각 링크의 결과가 미리 결정되어 있다고 직감적으로 믿습니다. 하지만 사실은 그렇지 않습니다. 전략 프로그램을 운영하는 동안 시장 데이터를 요청할 때 다양한 예상치 못한 상황이 발생합니다. 예를 들어, 일부 시장 인터페이스는 비정상적인 데이터를 반환합니다.
var depth = exchange.GetDepth()
// depth.Asks[0].Price < depth.Bids[0].Price 卖一价格低于了买一价格,这种情况不可能存在于盘面上,
// 因为卖出的价格低于买入的价格,必定已经成交了。
// depth.Bids[n].Amount = 0 订单薄买入列表第n档,订单量为0
// depth.Asks[m].Price = 0 订单薄卖出列表第m档,订单价格为0
또는 exchange.GetDepth()가 null 값을 직접 반환합니다.
이런 이상한 상황은 많이 있습니다. 따라서 이러한 예측 가능한 문제에 대해 해당 처리가 이루어져야 하며, 이러한 유형의 처리 솔루션을 내결함성 처리라고 합니다.
일반적인 장애 허용 방식은 데이터를 삭제하고 다시 검색하는 것입니다.
예를 들어:
function main () {
while (true) {
onTick()
Sleep(500)
}
}
function GetTicker () {
while (true) {
var ticker = exchange.GetTicker()
if (ticker.Sell > ticker.Buy) { // 以 检测卖一价格是不是小于买一价这个错误的容错处理为例,
// 排除这个错误,当前函数返回 ticker 。
return ticker
}
Sleep(500)
}
}
function onTick () {
var ticker = GetTicker() // 确保获取到的 ticker 不会存在 卖一价格小于买一价格这种数据错误的情况。
// ... 具体的策略逻辑
}
예측 가능한 다른 내결함성 프로세스도 비슷한 방식으로 처리할 수 있습니다. 설계 원칙은 잘못된 데이터를 사용하여 전략적 논리를 구축해서는 안 된다는 것입니다.
K-라인 데이터 수집, 호출:
var r = exchange.GetRecords()
획득된 K-라인 데이터는 다음과 같은 배열입니다.
[
{"Time":1562068800000,"Open":10000.7,"High":10208.9,"Low":9942.4,"Close":10058.8,"Volume":6281.887000000001},
{"Time":1562072400000,"Open":10058.6,"High":10154.4,"Low":9914.5,"Close":9990.7,"Volume":4322.099},
...
{"Time":1562079600000,"Open":10535.1,"High":10654.6,"Low":10383.6,"Close":10630.7,"Volume":5163.484000000004}
]
각 중괄호를 볼 수 있습니다{}여기에는 시간, 시작 가격(open), 최고 가격(high), 최저가(low), 종가(close), 거래량(volume)이 포함됩니다.
이것은 촛대입니다. 일반적으로 K-라인 데이터는 MA 이동 평균, MACD 등의 지표를 계산하는 데 사용됩니다.
K-라인 데이터를 매개변수(원자재 데이터)로 입력한 후, 지표 매개변수를 설정하고 지표 데이터의 함수를 계산하는데, 이를 지표 함수라고 합니다.
Inventor Quantitative Trading Platform에는 다양한 지표 기능이 있습니다.
예를 들어, 이동 평균 지표를 계산할 때 전달받은 K-라인 데이터의 다양한 기간을 기반으로 해당 기간의 이동 평균을 계산합니다. 예를 들어, 일일 K-라인 데이터가 전달되면(한 K-라인 막대가 하루를 나타냄) 계산된 지표는 일일 이동 평균입니다. 마찬가지로 이동 평균 지표 함수에 전달된 K-라인 데이터가 1시간 기간인 경우 계산된 지표는 1시간 이동 평균입니다.
일반적으로 지표를 계산할 때 종종 문제를 간과합니다. 5일 이동 평균 지표를 계산하려면 먼저 일일 K-라인 데이터를 준비합니다.
var r = exchange.GetRecords(PERIOD_D1) // 给GetRecords 函数传入参数 PERIOD_D1就是指定获取日K线,
// 具体函数使用可以参看:https://www.fmz.com/api#GetRecords
일별 K-line 데이터를 사용하면 이동 평균 지표를 계산할 수 있습니다. 5일 이동 평균을 계산하려면 지표 함수의 지표 매개변수를 5로 설정해야 합니다.
var ma = TA.MA(r, 5) // TA.MA() 就是指标函数,用来计算均线指标,第一个参数设置刚才获取的日K线数据r,
// 第二个参数设置5,计算出来的就是5日均线,其它指标函数同理。
우리는 잠재적인 문제를 간과했습니다. r-day K-line 데이터의 K-line 막대 수가 5개 미만이면 어떨까요? 유효한 5일 이동 평균 지표를 계산할 수 있을까요? 답은 확실히 ‘아니요’입니다. 이동평균선 지표는 특정 개수의 K-라인 막대의 종가 평균을 구하는 것입니다.

따라서 K-line 데이터와 지표 함수를 이용하여 지표 데이터를 계산하기 전에 K-line 데이터의 K-line 열의 개수가 지표 계산 조건(지표 매개변수)을 만족하는지 여부를 판단해야 한다.
따라서 5일 이동평균을 계산하기 전에 판단을 내릴 필요가 있습니다. 전체 코드는 다음과 같습니다.
function CalcMA () {
var r = _C(exchange.GetRecords, PERIOD_D1) // _C() 是容错函数,目的就是避免 r 为 null , 具体可以查询文档:https://www.fmz.com/api#_C
if (r.length > 5) {
return TA.MA(r, 5) // 用均线指标函数 TA.MA 计算出均线数据,做为函数返回值,返回。
}
return false
}
function main () {
var ma = CalcMA()
Log(ma)
}

백테스팅 결과: [null,null,null,null,4228.7,4402.9400000000005, … ]
계산된 5일 이동평균 지표 중 처음 네 개는 K-라인 열의 개수가 5개 미만이어서 평균을 계산할 수 없기 때문에 null인 것을 알 수 있습니다. 5번째 촛대를 통해 계산이 가능합니다.
전략을 작성할 때, 각 K-라인 사이클이 완료될 때마다 일부 연산을 처리하거나 일부 로그를 인쇄해야 하는 상황이 종종 발생합니다. 어떻게 이런 기능을 구현할 수 있을까? 프로그래밍 경험이 없는 초보자의 경우, 어떤 메커니즘을 사용하여 처리해야 할지 생각하지 못할 수도 있습니다. 여기서 우리는 여러분에게 몇 가지 팁을 직접 알려드리겠습니다.
K-라인 데이터에서 시간 속성으로 시작하여 K-라인 열 사이클이 완료되었는지 판단할 수 있습니다. K-라인 데이터를 얻을 때마다 마지막 K-라인 열의 데이터에서 시간 속성을 판단합니다. 이 K-라인 데이터의. 이 속성 값이 변경되었는지 여부. 변경된 경우 새 K-라인 열이 생성되었음을 의미합니다(새로 생성된 K-라인 열의 이전 K-라인 열 주기가 생성되었음을 증명합니다. 완료되었습니다). 변경되지 않은 경우, 새로운 캔들스틱이 생성되지 않았다는 의미입니다(현재 마지막 캔들스틱 주기가 아직 완료되지 않았습니다).
그래서 우리는 캔들스틱 데이터의 마지막 캔들스틱 열의 시간을 기록하는 변수가 필요합니다.
var r = exchange.GetRecords()
var lastTime = r[r.length - 1].Time // lastTime 用来记录最后一根K线柱的时间。
실제 응용 프로그램에서는 일반적으로 구조가 다음과 같습니다.
function main () {
var lastTime = 0
while (true) {
var r = _C(exchange.GetRecords)
if (r[r.length - 1].Time != lastTime) {
Log("新K线柱产生")
lastTime = r[r.length - 1].Time // 一定要更新 lastTime ,这个至关重要。
// ... 其它处理逻辑
// ...
}
Sleep(500)
}
}

백테스트에서 K-라인 기간이 일로 설정되어 있는 것을 볼 수 있습니다(exchange.GetRecords 함수는 매개변수를 지정하지 않고 호출되며, 백테스트에 따라 설정된 K-라인 기간이 기본 매개변수임). 새로운 K-라인 열이 나타나고 A 로그로 인쇄됩니다.
전략이 거래소 인터페이스에 접근하는 데 걸리는 시간을 표시하거나 제어하려면 다음 코드를 사용할 수 있습니다.
function main () {
while (true) {
var beginTime = new Date().getTime()
var ticker = exchange.GetTicker()
var endTime = new Date().getTime()
LogStatus(_D(), "GetTicker() 函数耗时:", endTime - beginTime, "毫秒")
Sleep(1000)
}
}
간단히 말해서, GetTicker 함수를 호출한 후 기록된 타임스탬프를 호출 전 타임스탬프에서 빼서 경과한 밀리초 수, 즉 GetTicker 함수가 실행되어 결과를 반환하는 데 걸리는 시간을 계산합니다.
숫자적 상한이 필요한 경우 일반적으로 Math.min을 사용하여 제한합니다.
예를 들어, 매도 주문을 할 때 주문 금액은 계좌에 있는 코인 수보다 많아서는 안 됩니다. 계좌에 있는 사용 가능한 코인의 수보다 큰 경우 주문을 할 때 오류가 보고됩니다.
일반적으로 다음과 같이 제어합니다. 예를 들어, 0.2코인에 대한 매도 주문을 하려고 합니다.
var planAmount = 0.2
var account = _C(exchange.GetAccount)
var amount = Math.min(account.Stocks, planAmount)
이를 통해 주문 금액이 계좌에 있는 코인 수량을 초과하지 않도록 보장할 수 있습니다.
마찬가지로, Math.max는 값의 하한을 보장하는 데 사용됩니다. 이것은 일반적으로 어떤 종류의 시나리오에 적용됩니까? 일반적으로 거래소는 특정 거래 쌍에 대해 최소 주문 수량 제한을 두고 있습니다. 주문 수량이 이 최소 주문 수량보다 낮으면 주문이 거부됩니다. 이 주문은 실패할 것입니다. BTC의 최소 주문 수량은 일반적으로 0.01이라고 가정합니다. 때로는 거래 전략에 따라 주문 수량이 0.01보다 작다고 계산될 수 있으므로 Math.max를 사용하여 최소 주문 수량을 보장할 수 있습니다.
사용할 수 있습니다_N() 함수나 SetPrecision 함수를 사용하여 정밀도를 제어합니다.
SetPrecision() 함수는 한 번만 설정하면 되며, 시스템이 주문 수량과 가격 값에서 초과되는 소수점 자릿수를 자동으로 잘라냅니다.
_N() 함수는 값을 소수점 이하 자릿수까지 잘라내는 데 사용됩니다(정밀도 제어)
예를 들어:
var pi = _N(3.141592653, 2)
Log(pi)
파이 값은 소수점 둘째 자리까지 잘라서 3.14가 됩니다.
자세한 내용은 API 문서를 참조하세요.
이 메커니즘을 사용하면 타임스탬프 감지 방법을 사용하여 예약된 작업이 마지막으로 완료된 타임스탬프를 뺀 현재 타임스탬프를 확인하고 경과 시간을 실시간으로 계산할 수 있습니다. 경과 시간이 특정 설정 시간 길이를 초과하면 그 후 새로운 작업이 실행됩니다.
예를 들어, 고정 투자 전략에 사용됩니다.
var lastActTime = 0
var waitTime = 1000 * 60 * 60 * 12 // 一天的毫秒数
function main () {
while (true) {
var nowTime = new Date().getTime()
if (nowTime - lastActTime > waitTime) {
Log("执行定投")
// ... 具体的定投操作,买入操作。
lastActTime = nowTime
}
Sleep(500)
}
}
이것은 간단한 예입니다.
발명자의 양자화된 _G() 함수와 종료 저장 함수를 사용하면 진행 상황을 종료하고 저장하고, 다시 시작하여 상태를 자동으로 복원하는 전략을 설계하는 것이 매우 편리합니다.
var hold = {
price : 0,
amount : 0,
}
function main () {
if (_G("hold")) {
var ret = _G("hold")
hold.price = ret.price
hold.amount = ret.amount
Log("恢复 hold:", hold)
}
var count = 1
while (true) {
// ... 策略逻辑
// ... 策略运行中,可能开仓,交易,把开仓的持仓价格赋值给 hold.price ,开仓的数量赋值给 hold.amount,用以记录持仓信息。
hold.price = count++ // 模拟一些数值
hold.amount = count/10 // 模拟一些数值
Sleep(500)
}
}
function onexit () { // 点击机器人上的停止按钮,会触发执行这个函数,执行完毕机器人停止。
_G("hold", hold)
Log("保存 hold:", JSON.stringify(hold))
}

로봇이 멈출 때마다 홀드 객체의 데이터가 저장되는 것을 볼 수 있습니다. 재시작할 때마다 데이터가 읽히고 홀드 값이 이전 정지 시 상태로 복원됩니다. 물론, 위의 내용은 간단한 예입니다. 실제 전략에 사용된다면 전략에서 복원해야 하는 핵심 데이터(일반적으로 계정 정보, 포지션, 이익 값, 거래 방향 등)에 따라 설계해야 합니다. .). 물론, 복원 여부를 결정하기 위해 몇 가지 조건을 설정할 수도 있습니다.
위의 내용은 전략을 개발하기 위한 몇 가지 팁입니다. 초보자와 전략 개발자에게 도움이 되기를 바랍니다! 가장 빠르게 향상되는 방법은 연습하는 것입니다! 여러분 모두 지속적인 이익을 기원합니다.