거래 전략 개발 경험 이야기

저자:작은 꿈, 2019-08-06 17:15:13, 업데이트: 2023-10-20 20:06:49

img

거래 전략 개발 경험 이야기

이 문서의 주제는 전략 개발에 대한 몇 가지 경험과 간단한 팁을 소개하여 독자들이 거래 전략 개발에 대해 빠르게 이해할 수 있도록하는 것입니다. 이 같은 세부적인 문제들이 전략 설계에 있어서 발생했을 때, 합리적인 해결책을 즉시 생각해 낼 수 있다. 발명가들의 양적 거래 플랫폼을 강연, 테스트, 연습 플랫폼으로 사용하십시오. 전략 프로그래밍 언어: 자바스크립트 거래 시장: 블록체인 자산 시장 (BTC, ETH 등)

  • 데이터의 접근 및 처리

    일반적으로 전략 논리에 따라 다른 방법의 경우 다음과 같은 다른 인터페이스를 사용하여 시장 데이터를 얻을 수 있습니다. 일반적으로 전략의 거래 논리는 시장 데이터에 의해 구동됩니다.

    • 티커: 실시간 티크 시장을 검색합니다. 일반적으로 현재 최신 가격을 빠르게 얻을 수 있습니다, 한 가격에 구입, 한 가격에 판매.

    • 데프스 (GetDepth): 순서와 심층 시장에 대한 접근. 일반적으로 각 품종의 가격, 주문량 및 크기를 얻기 위해 사용됩니다. 이 프로젝트의 주요 목표는

    • 트레이드: 시장의 최근 거래 기록을 얻으십시오. 일반적으로 짧은 시간에 시장 행동을 분석하고 시장의 미세 변화를 분석하는 데 사용됩니다. 일반적으로 고주파 전략, 알고리즘 전략으로 사용됩니다.

    • 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
      

      또는 직접 교환.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 라인 데이터 사용

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

      이 그림은{}중간에는 시간, 오픈 가격, 최고 가격, 최저 가격, 폐쇄 가격, 거래량 등이 포함되어 있습니다. 이것은 K선 기둥입니다. 일반적인 K선 데이터는 지표를 계산하는 데 사용됩니다. 예를 들어: MA 평균선, MACD 등. K선 데이터를 매개 변수로 입력 (초자료 데이터) 하고 지표 매개 변수를 설정하여 지표 데이터의 함수를 계산합니다. 우리는 이것을 지표 함수라고 부릅니다. 발명자의 양적 거래 플랫폼에는 많은 지표 함수가 있습니다.

      예를 들어, 우리는 평선 지표를 계산하고, 우리가 입력한 K선 데이터의 주기에 따라 다른 평선을 계산하면, 해당 주기의 평선이 계산됩니다. 예를 들어, K선 데이터 (K선 기둥이 하루를 나타냅니다) 를 전송하면, 하루 평균을 계산합니다.

      일반적으로 우리가 지표를 계산할 때 종종 무시하는 문제 중 하나는, 만약 제가 5일 평균 지표를 계산해야 한다면, 우리는 먼저 K일선을 준비해야 합니다.

      var r = exchange.GetRecords(PERIOD_D1)  // 给GetRecords 函数传入参数 PERIOD_D1就是指定获取日K线,
                                              // 具体函数使用可以参看:https://www.fmz.com/api#GetRecords
      

      K일선 데이터를 가지고, 우리는 평선 지표를 계산할 수 있습니다. 우리는 5일선 지표를 계산할 것입니다. 우리는 지표 함수의 지표 매개 변수를 5로 설정해야 합니다.

      var ma = TA.MA(r, 5)        // TA.MA() 就是指标函数,用来计算均线指标,第一个参数设置刚才获取的日K线数据r,
                                  // 第二个参数设置5,计算出来的就是5日均线,其它指标函数同理。
      

      우리는 잠재적인 문제를 무시하고 있습니다. 만약 r일 K선 데이터에서 K선 기둥이 5개 미만이라면, 어떻게 하면 유효한 5일 평균 지표를 계산할 수 있을까요? 이 질문에 대한 답은 분명합니다. 왜냐하면 평선 지표는 K선 기둥의 일정한 수에 대한 종료 가격의 평균값이기 때문입니다.

      img

      따라서 K선 데이터, 지표 함수 계산 지표 데이터를 사용하기 전에, K선 데이터의 K선 기둥의 수가 지표 계산의 조건을 만족하는지 여부를 판단해야 합니다.

      그래서 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)
      }
      

      img

      이 사진이 보여준 것은 [null,null,null,null,4228.7,4402.9400000000005,... ]

      계산된 5일 평균자책점 지표를 볼 수 있는데, 첫 4개는 0이므로, K선 기둥이 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)
          }
      }
      

      img

      회수에서 볼 수 있듯이, K선 주기는 날짜 (exchange.GetRecords 함수 호출 때 매개 변수를 지정하지 않고, 회수 설정에 따라 K선 주기는 기본 매개 변수) 로 설정되어 있으며, 새로운 K선 기둥이 나타날 때마다 로그가 인쇄됩니다.

  • 수치 계산

    • 거래소 접속 인터페이스의 시간을 계산합니다.

      만약 당신이 전략적으로 거래소에 접근하는 인터페이스의 사용시간에 대해 특정 표시 또는 통제를 원한다면, 다음 코드를 사용할 수 있습니다:

      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 / Math.max로 숫자에 대한 상하위 제한

      값에 한계가 있으면 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)
      

      π의 값은 소수자 수를 단절하여 2자 소수를 유지합니다. 즉: 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))
      }
      

      img

      볼 수 있듯이, 로봇을 중지할 때마다 hold 객체에서 데이터가 저장되고, 다시 시작할 때마다 데이터를 읽고, hold의 숫자를 멈추기 전의 상태로 되돌립니다. 물론 이것은 간단한 예이며, 실제 전략에 사용되면 전략에서 복구해야 할 핵심 데이터 (일반적으로 계좌 정보, 보유, 수익 수, 거래 방향 등 정보) 에 따라 설계되어야합니다. 물론 복원 여부를 위한 조건도 설정할 수 있습니다.

여기 몇 가지 전략 개발 트릭이 있습니다. 초보자와 전략 개발자들에게 도움이 될 것으로 기대합니다. "이번 강연은 여러분들에게 많은 행운을 기원합니다".


관련

더 많은

위크스1ao이 앱은 API에 대해 잘 알지 못하는 초보자에게 적합합니다. 또한 우리의 플랫폼이 더 높은 버전을 지원하는지 여부를 문의하십시오. 예를 들어?

마이케오멘트님 감사합니다! 멘트 선생님, 정말 문무 양쪽 모두 아, 프로그래밍 기술이 높고, 글 문체도 좋고, 존경하는 것이 좋습니다!

작은 꿈안녕하세요, 현재 ES8 표준을 지원합니다.

작은 꿈FMZ의 양성 지원에 감사합니다!