FMZをベースに量化されたオーダーシンクロマネジメントシステムの設計 (1)

作者: リン・ハーン小さな夢, 作成日:2022-02-14 19:46:30, 更新日:2023-09-15 20:44:11

img

FMZをベースに量化されたオーダーシンクロマネジメントシステムの設計 (1)

FMZ資料庫の過去の記事では,いくつかのオーダー,収納,同期戦略を設計しました.

参考アカウントと同期アカウントを1つの戦略にまとめて,オーダーの実現管理,収納同期を図る.今日,少し違ったデザインを試し,FMZ量化取引プラットフォームの強力な拡張APIインターフェースをベースに,オーダーの同期管理システムを設計します.

デザインのアイデア

まず,いくつかの良い提案や要求が必要です. 上記の2つの先行オーダーや,同期戦略のいくつかの明らかな痛みは,一緒に議論しましょう.

  • 1, 同期戦略の実用化者は,参照アカウントの取引所API KEY,同期アカウントの取引所API KEY を必要とする. この使用シナリオの問題は,自分の他の取引所アカウントが自分のアカウントをフォローすることは問題ではない.しかし,参照アカウントと同期アカウントが所有者でないシナリオでは,問題はあります.同期アカウントの所有者は,セキュリティ上の理由で,自分の取引所アカウントのAPI KEYを提供することを拒否することがあります.しかし,API KEYを提供しないと,単一の取引をどのように同期することができますか.

    解決策は FMZの拡張APIインターフェイスを使用して,同期アカウントの所有者 (追記者) は,FMZ量化取引プラットフォームに登録し,このプログラムで設計されたシステムで1つのポリシーを実行するだけです.订单同步管理系统(Synchronous Server)FMZの拡張API KEY (注:取引所のアカウントのAPI KEYではない) と,オーダーシンクロノースサーバーのリアルディスクIDを参照アカウントの所有者に提供できます. 参照アカウント所有者の実ディスク (本文で設計されたシステム) は,订单同步管理系统类库(Single Server)) 信号を送信すると,同期口座所有者の実機は取引信号を受け取り,自動的に下注されます.

  • 2,多くの開発者が比較して良い戦略を持っているが,上記2つの先行オーダー,または持参同期戦略を使用することはできません. それは,自分の戦略とこれらの同期戦略を統合する必要があるため,戦略は大きく変更され,苦労する必要があります. 自分の成熟した戦略の一部を直接オーダー同期機能にアップグレードするための良い方法がありますか? 解決策は 注文同期テンプレートクラスバックリ (本文で設計したシステム) を設計できます.订单同步管理系统类库(Single Server)参照アカウントの所有者 (バンド) は,このテンプレートクラスを自分のポリシーに直接埋め込むことで,注文,保管同期機能を実現することができます.

  • 3 余分な実盤を減らします. 最後の痛みは,上記の2つの先行オーダー,保持同期戦略を使用した場合,さらに実体モニタリング参照口座 (帯持口座) を開く必要があることです. 解決策は テンプレートクラスライブラリを使用して,機能を参照アカウントポリシーに埋め込む.

このシステムには2つの部分があります. 1, 注文同期管理システムクラスデータベース (Single Server) 2, 同期注文管理システム (Synchronous Server)

デザインを始めてください.

デザイン1: オーダーシンクロ管理システムクラスデータベース (Single Server)

注意,これは策略ではなく,FMZのテンプレートデータベースであり,テンプレートデータベースの概念はFMZ APIのドキュメントで検索できます.

模板クラスライブラリコード:

// 全局变量
var keyName_label = "label"
var keyName_robotId = "robotId"
var keyName_extendAccessKey = "extendAccessKey"
var keyName_extendSecretKey = "extendSecretKey"
var fmzExtendApis = parseConfigs([config1, config2, config3, config4, config5])
var mapInitRefPosAmount = {}

function parseConfigs(configs) {
    var arr = []
    _.each(configs, function(config) {
        if (config == "") {
            return 
        }
        var strArr = config.split(",")
        if (strArr.length != 4) {
            throw "configs error!"
        }
        var obj = {}
        obj[keyName_label] = strArr[0]
        obj[keyName_robotId] = strArr[1]
        obj[keyName_extendAccessKey] = strArr[2]
        obj[keyName_extendSecretKey] = strArr[3]
        arr.push(obj)
    })
    return arr 
}

function getPosAmount(pos, ct) {
    var longPosAmount = 0
    var shortPosAmount = 0
    _.each(pos, function(ele) {
        if (ele.ContractType == ct && ele.Type == PD_LONG) {
            longPosAmount = ele.Amount
        } else if (ele.ContractType == ct && ele.Type == PD_SHORT) {
            shortPosAmount = ele.Amount
        }
    })
    var timestamp = new Date().getTime()
    return {ts: timestamp, long: longPosAmount, short: shortPosAmount}
}

function sendCommandRobotMsg (robotId, accessKey, secretKey, msg) {
    // https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[186515,"ok12345"]
    var url = "https://www.fmz.com/api/v1?access_key=" + accessKey + "&secret_key=" + secretKey + "&method=CommandRobot&args=[" + robotId + ',"' + msg + '"]'
    Log(url)
    var ret = HttpQuery(url)
    return ret 
}

function follow(nowPosAmount, symbol, ct, type, delta) {
    var msg = ""
    var nowAmount = type == PD_LONG ? nowPosAmount.long : nowPosAmount.short
    if (delta > 0) {
        // 开仓
        var tradeDirection = type == PD_LONG ? "buy" : "sell"
        // 发送信号
        msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)        
    } else if (delta < 0) {
        // 平仓
        var tradeDirection = type == PD_LONG ? "closebuy" : "closesell"
        if (nowAmount <= 0) {
            Log("未检测到持仓")
            return 
        }
        // 发送信号
        msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)
    } else {
        throw "错误"
    }
    if (msg) {
        _.each(fmzExtendApis, function(extendApiConfig) {
            var ret = sendCommandRobotMsg(extendApiConfig[keyName_robotId], extendApiConfig[keyName_extendAccessKey], extendApiConfig[keyName_extendSecretKey], msg)
            Log("调用CommandRobot接口,", "label:", extendApiConfig[keyName_label], ", msg:", msg, ", ret:", ret)
            Sleep(1000)
        })
    }
}

$.PosMonitor = function(exIndex, symbol, ct) {    
    var ts = new Date().getTime()
    var ex = exchanges[exIndex]
    // 判断ex类型
    var exName = ex.GetName()
    var isFutures = exName.includes("Futures_")
    var exType = isFutures ? "futures" : "spot"
    if (!isFutures) {
        throw "仅支持期货跟单"
    }

    if (exType == "futures") {
        // 缓存 symbol ct
        var buffSymbol = ex.GetCurrency()
        var buffCt = ex.GetContractType()

        // 切换到对应的交易对、合约代码
        ex.SetCurrency(symbol)
        if (!ex.SetContractType(ct)) {
            throw "SetContractType failed"
        }

        // 监控持仓
        var keyInitRefPosAmount = "refPos-" + exIndex + "-" + symbol + "-" + ct    // refPos-exIndex-symbol-contractType
        var initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        if (!initRefPosAmount) {
            // 没有初始化数据,初始化          
            mapInitRefPosAmount[keyInitRefPosAmount] = getPosAmount(_C(ex.GetPosition), ct)
            initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        }

        // 监控
        var nowRefPosAmount = getPosAmount(_C(ex.GetPosition), ct)
        // 计算仓位变动
        var longPosDelta = nowRefPosAmount.long - initRefPosAmount.long
        var shortPosDelta = nowRefPosAmount.short - initRefPosAmount.short

        // 检测变动
        if (!(longPosDelta == 0 && shortPosDelta == 0)) {
            // 执行多头动作
            if (longPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "执行多头跟单,变动量:", longPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_LONG, longPosDelta)
            }
            // 执行空头动作
            if (shortPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "执行空头跟单,变动量:", shortPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_SHORT, shortPosDelta)
            }

            // 执行跟单操作后,更新
            mapInitRefPosAmount[keyInitRefPosAmount] = nowRefPosAmount
        }

        // 恢复 symbol ct
        ex.SetCurrency(buffSymbol)
        ex.SetContractType(buffCt)
    } else if (exType == "spot") {
        // 现货
    }
}

$.getTbl = function() {
    var tbl = {
        "type" : "table", 
        "title" : "同步数据", 
        "cols" : [], 
        "rows" : []
    }
    // 构造表头
    tbl.cols.push("监控账户:refPos-exIndex-symbol-contractType")
    tbl.cols.push(`监控持仓:{"时间戳":xxx,"多头持仓量":xxx,"空头持仓量":xxx}`)
    _.each(fmzExtendApis, function(extendApiData, index) {
        tbl.cols.push(keyName_robotId + "-" + index)
    })
    
    // 写入数据
    _.each(mapInitRefPosAmount, function(initRefPosAmount, key) {
        var arr = [key, JSON.stringify(initRefPosAmount)]
        _.each(fmzExtendApis, function(extendApiData) {
            arr.push(extendApiData[keyName_robotId])
        })
        tbl.rows.push(arr)
    })

    return tbl
}

// 引用该模板类库的策略调用范例
function main() {
    // 清除所有日志
    LogReset(1)

    // 切换到OKEX 模拟盘测试
    exchanges[0].IO("simulate", true)

    // 设置合约
    exchanges[0].SetCurrency("ETH_USDT")
    exchanges[0].SetContractType("swap")

    // 定时交易时间间隔
    var tradeInterval = 1000 * 60 * 3        // 三分钟交易一次,用于观察跟单信号
    var lastTradeTS = new Date().getTime()
    
    while (true) {
        // 策略其它逻辑...

        // 用于测试的模拟交易触发
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("模拟带单策略发生交易,持仓变化", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // 使用模板的接口函数
        $.PosMonitor(0, "ETH_USDT", "swap")    // 可以设置多个监控,监控带单策略上的不同的exchange对象  
        var tbl = $.getTbl()
        
        // 显示状态栏
        LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}

FMZのプラットフォーム上のプログラム化取引戦略が引用されたとき,このデータベースは,订单同步管理系统类库(Single Server)テンプレートクラスライブラリの後ろに. このシートには,次の関数を使用できます.

  • $PosMonitor をクリックします この機能の役割は,取引所の対象の保有変動を監視する方針であり,その後,テンプレート:オーダーシンクロマネジメントシステム (Single Server) のパラメータに設定されたリアルディスク取引信号を送信する.

  • $2.getTbl は 監視された同期データを返します.

使用例は: 注文同期管理システムクラスバックリ (Single Server) のテンプレートmainこの関数は,

// 引用该模板类库的策略调用范例
function main() {
    // 清除所有日志
    LogReset(1)

    // 切换到OKEX 模拟盘测试
    exchanges[0].IO("simulate", true)

    // 设置合约
    exchanges[0].SetCurrency("ETH_USDT")
    exchanges[0].SetContractType("swap")

    // 定时交易时间间隔
    var tradeInterval = 1000 * 60 * 3        // 三分钟交易一次,用于观察跟单信号
    var lastTradeTS = new Date().getTime()
    
    while (true) {
        // 策略其它逻辑...

        // 用于测试的模拟交易触发
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("模拟带单策略发生交易,持仓变化", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // 使用模板的接口函数
        $.PosMonitor(0, "ETH_USDT", "swap")    // 可以设置多个监控,监控带单策略上的不同的exchange对象  
        var tbl = $.getTbl()
        
        // 显示状态栏
        LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}

テンプレートクラスバックリ自体は,通常,テンプレートクラスバックリをテストするために使用される ポリシーディスクを作成することもできます. 例えば,このテンプレートのテストです. テンプレートの中で理解することができます.mainこの関数は,あなたの自分の戦略です.mainこの関数は,

テストコードは,OKEX アナログディスクを使ってテストするために書かれたもので,FMZで OKEX アナログディスクのAPI KEYを参照アカウントとして設定し,メイン関数でアナログディスクに切り替える.その後,取引対をETH_USDTに設定し,契約を永久に設定します.その後,while ループに入ります.ループの3分ごとに1回注文取引を行い,アナログ戦略取引を誘発するために使用します.while ループで呼び出されます.$.PosMonitor(0, "ETH_USDT", "swap")この関数の呼び出しの最初のパラメータは,monitor exchanges[0]を表示し,この取引所のオブジェクトを監視し,ETH_USDT取引対,スワップ契約を呼び出す.$.getTbl()グラフの情報を取得し,それを使用しますLogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")グラフのデータを状態バーに表示します.

テンプレートに引用する戦略で使えばいいのです$.PosMonitor(0, "ETH_USDT", "swap")策略には,ある品種を監視し,その品種がメッセージを送信する動きを監視する機能が与えられます.

テストの前に説明してください.订单同步管理系统类库(Single Server)戦略のパラメータ設計: テンプレートのインターフェース関数を活用して,ある戦略をアップグレードし,バンド機能を持つようにする方法を説明しました. メールは誰に送るかという問題は订单同步管理系统类库(Single Server)設定されたパラメータです.

img

パラメータは5つあり,最大5つのプッシュをサポートしている (追加が必要で自律的に拡張できる),パラメータは空の文字列でデフォルトで処理されません.

  • レーベル 同期アカウントのタグは,特定のアカウントにタグ付けされ,名前を任意に設定することができます.

  • ロボットId リアルディスクIDは,同期アカウントの所有者が作成したものです订单同步管理系统(Synchronous Server)リアルディスクのID.

  • アクセスキー FMZの拡張APIのアクセスキー

  • シークレットキー FMZの拡張APIの secretKey

簡単なテストを行います. 簡単なテストを行います.

注文同期管理システムクラスバックリ (Single Server) の実装台を実行する:

img

注文同期管理システム (Synchronous Server) のディスクに信号が受信されました. 同期サーバー (Synchronous Server) の設計はまだ完了していませんが,まずは簡単なコードで実行します. 取引を行わず,信号を印刷します.

注文同期管理システム (Synchronous Server) 暫定コード:

function main() {
    LogReset(1)
    while (true) {
        var cmd = GetCommand()
        if (cmd) {
            // cmd: ETH_USDT,swap,buy,1
            Log("cmd: ", cmd)
        }
        Sleep(1000)
    }
}

img

シンクロアカウント所有者の実機にメッセージが受信されているのが見えます.ETH_USDT,swap,buy,1│ │ 取引先の情報や契約コード,取引方向,取引量などに合わせて,次のステップを自動的に実行できます.

現在订单同步管理系统(Synchronous Server)デザインについては,次の記事で詳しく説明します.


関連性

もっと

ミンジ1005リストを実行するには 2 つのディスクが必要です. 1 つはクラスディスク, 1 つはオーダー管理システムディスク.

ミンジ1005設定が間違っています

アール逆行リストのパラメータを変更する必要があります.

アールこの2つのディスクを組み合わせて使えますか? 信号を送るディスクと受信するディスクです.

小さな夢このクラスは,バンドライダーのポリシー行に直接埋め込むツールであり,バンド機能が機能すると,設定されたバンドライダーのアカウントにメッセージを送信し,バンドライダーはメッセージを受け取ります. 簡単に言うと,このような状況です.

小さな夢記事,配置情報:タグ,実ディスクID,accesskey,secretkey を参照してください. このエラーを報告するのは,あなたの情報が正しく配置されていないことを意味します.

ミンジ1005誤り configs error!, 注文同期管理システムデータベース (Single Server) に,帯主ディスクと2つのKEYをすべて埋め込み,その後実ディスクに注文同期管理システムデータベース (Single Server) を参照し,エラー,エラー configs error! を返します.

ミンジ1005設定が間違っています!

小さな夢誤った情報を報告する方法を確認します.

小さな夢戦略を変える必要がある.

小さな夢オープンソースで,必要に応じて変更することもできます.