avatar of 发明者量化-小小梦 发明者量化-小小梦
フォロー ダイレクトメッセージ
4
フォロー
1271
フォロワー

定量的な旅はFMZから始まる

作成日:: 2025-04-18 09:31:42, 更新日:: 2025-04-26 11:50:01
comments   0
hits   937

定量的な旅はFMZから始まる

導入

フレームワークを構築するために徹夜でコードを書いたり、UI を設計したり、さまざまな設計の詳細やメカニズムを自分で作成したりしなくても、簡単にクオンツ取引を始められ、すぐに始めることができると考えたことはありますか? FMZ 定量プラットフォームではすべてが可能になります。高度なプログラミングの知識は必要ありませんし、複雑な導入プロセスについて心配する必要もありません。必要なのは、コンピューターとアカウントだけです。これで、「どこでも」クオンツの旅を始めることができます。この記事では、FMZ をゼロからすぐに使い始め、自動取引の魅力を体験し、データと戦略を使用して市場のリズムをマスターする方法を説明します。効率性の向上を目指す初心者でもベテランでも、この取り組みは試してみる価値があります。

定量取引初心者の混乱

私はプラットフォームの初心者とよくコミュニケーションしたりチャットしたりしています。定量取引の初心者は、通常、完全な設計プロセスに混乱します。取引のアイデアが浮かんだとき、どこから始めればいいのかわからず、圧倒されてしまうことがよくあります。

以下について混乱しています:

  • オープニングポジションとクロージングポジションの設計方法
  • 収益計算の設計方法
  • 取引の進捗を再開し継続するための戦略を設計する方法
  • 戦略チャート表示の設計方法
  • 戦略的なインタラクション制御を設計する方法

上記の混乱を一緒に解決しましょう。

デザインの説明

定量取引の世界では、戦略設計は終わりのない探索の旅となることがよくあります。インジケーターを作成しようとしたり、盲目的に売買シグナルに従おうとしたことがあるかもしれませんが、本当に効果を発揮するのは、「可視性、調整性、安定性」を備えた戦略システムです。 FMZ定量プラットフォームを基盤に、「時間通りに進む」という実践的な体験をすることができます。パラメータ設定、チャート表示からインタラクティブ機能、損益計算まで、戦略の設計要件を完全に満たすシンプルな戦略を構築します。

戦略のアイデアは、ATR、段階的なグリッドポジション構築ロジック(ロングとショート双方向)、ATR適応型ボラティリティ計算、およびポジション清算ロジック(市場が中心軸に反転した場合)に基づいた段階的なポジション増加戦略です。

この戦略は、次の設計要件に基づいています。

さまざまなレベルでの価格ブレイクスルーに応じてポジションを追加したり、ポジションをクローズしたりする

位置の段階的な増加を制御するために 2 つの配列を設定します。

var arrUp = null 
var arrDown = null 

ポジションを追加するたびに、そのポジション情報が配列にプッシュされるため、ポジションの制御が容易になり、戦略のリアルタイム インターフェイスにデータを表示できるようになります。

価格のブレイクスルーレベルに応じてポジションを開いたり閉じたりします。簡潔にするために、ポジションの開始と終了の両方で、シンプルで効果的な成行注文を使用します。

            if (close > up && i >= arrUp.length && !isPaused) {
                var id = exchange.CreateOrder(symbol, "sell", -1, tradeAmount)
                if (!id) {
                    Log("下单失败")
                    continue
                }
                arrUp.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
                _G("arrUp", arrUp)
                arrSignal.push([r[r.length - 1].Time, "short", close, tradeAmount])
                Log([r[r.length - 1].Time, "short", close, tradeAmount], "@")
            } else if (close < down && i >= arrDown.length && !isPaused) {
                var id = exchange.CreateOrder(symbol, "buy", -1, tradeAmount)
                if (!id) {
                    Log("下单失败")
                    continue 
                }
                arrDown.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
                _G("arrDown", arrDown)
                arrSignal.push([r[r.length - 1].Time, "long", close, tradeAmount])
                Log([r[r.length - 1].Time, "long", close, tradeAmount], "@")
            } else if (((arrUp.length > 0 && close < mid) || (arrDown.length > 0 && close > mid)) && !isPaused) {
                clear(pos, r)
            }

インベントリをクリアし、関数を使用して処理します。一部のデータ構造は、在庫がクリアされるたびにリセットする必要があるため、対話型モジュールで再利用できるように、クリア関数を関数にカプセル化する必要があります。

function clear(positions, r) {
    var close = r[r.length - 1].Close
    for (var p of positions) {
        if (p.Type == PD_LONG) {
            var id = exchange.CreateOrder(symbol, "closebuy", -1, p.Amount)
            if (!id) {
                Log("下单失败")
                continue 
            }
            arrSignal.push([r[r.length - 1].Time, "closelong", close, p.Amount])
            Log([r[r.length - 1].Time, "closelong", close, p.Amount], "@")
        } else if (p.Type == PD_SHORT) {
            var id = exchange.CreateOrder(symbol, "closesell", -1, p.Amount)
            if (!id) {
                Log("下单失败")
                continue 
            }
            arrSignal.push([r[r.length - 1].Time, "closeshort", close, p.Amount])
            Log([r[r.length - 1].Time, "closeshort", close, p.Amount], "@")
        }
    }
    arrUp = []
    arrDown = []
    _G("arrUp", arrUp)
    _G("arrDown", arrDown)
    var profit = calcProfit()
    LogProfit(profit)
}

ポジションの割り当てを段階的に

複数のレベルに分かれており、最大レベルは maxRatio です。各層ごとに異なる価格しきい値が計算されます。

        for (var i = 0; i < maxRatio; i++) {                        
            var up = open + atr[atr.length - 1] * (i + 1)
            var mid = open
            var down = open - atr[atr.length - 1] * (i + 1)
            atrs.push([open, (i + 1), atr])
            
            var tradeAmount = baseAmount * Math.pow(2, i)
            if (isAmountForUSDT) {
                tradeAmount = tradeAmount * 1.05 / close
            }
            tradeAmount = _N(tradeAmount, amountPrecision)

            var balance = acc.Balance
            if (balance - initAcc.Equity * reserve < tradeAmount * close) {
                continue 
            }
        // ...
        }

動的なパラメータ調整、一時停止操作、クイッククリアなどのインタラクションをサポート

インタラクティブ機能の設計、インベントリのクリア、一時停止、一時停止解除、パラメータの変更など。FMZ でのインタラクションの設計は非常に便利で、プラットフォームは多くのインタラクティブ コントロールを提供します。戦略にインタラクティブなコントロールを追加し、戦略コードでメッセージを受信したときにさまざまな認識および処理コードを記述するだけです。

        var cmd = GetCommand()
        if (cmd) {
            Log("交互指令:", cmd)
            var arrCmd = cmd.split(":")
            if (arrCmd.length == 2) {
                var strCmd = arrCmd[0]
                var param = parseFloat(arrCmd[1])
                if (strCmd == "atrPeriod") {
                    atrPeriod = param
                    Log("修改ATR参数:", atrPeriod)
                }
            } else {
                if (cmd == "isPaused" && !isPaused) {
                    isPaused = true
                    Log("暂停交易")
                } else if (cmd == "isPaused" && isPaused) {
                    isPaused = false 
                    Log("取消暂停交易")
                } else if (cmd == "clearAndPaused") {
                    clear(pos, r)
                    isPaused = true
                    Log("清仓、暂停交易")
                }
            }
        }

開閉リマインダー機構付き

戦略を開いたり閉じたりするとき、メッセージを簡単にプッシュすることができます。郵便、FMZ APP、サードパーティインターフェースなど。

Log([r[r.length - 1].Time, "long", close, tradeAmount], "@")  // 消息推送

プッシュ通知を受信する(FMZ APP および他のアプリもプッシュ通知を受信します):

定量的な旅はFMZから始まる

リアルタイムの統計と利益とポジションの表示

損益計算関数はポジションが決済されるたびに呼び出され、損益を計算して損益曲線を出力します。

function calcProfit() {
    var initAcc = _G("initAcc")
    var nowAcc = _C(exchange.GetAccount)
    var profit = nowAcc.Equity - initAcc.Equity
    return profit
}

状態の永続性(ブレークポイントの回復)をサポート

FMZを使用する_G()機能により、戦略進捗回復機構の設計が容易になります。

    if (isReset) {
        _G(null)
        LogProfitReset()
        LogReset(1)
        c.reset()
    }

    arrUp = _G("arrUp")
    if (!arrUp) {
        arrUp = []
        _G("arrUp", arrUp)
    }

    arrDown = _G("arrDown")
    if (!arrDown) {
        arrDown = []
        _G("arrDown", arrDown)
    }

金額による注文の設計

契約を取引する場合、注文インターフェースの注文数量は契約数なので、ユーザーはUsの数で注文を出す方法をよく尋ねます。

            if (isAmountForUSDT) {
                tradeAmount = tradeAmount * 1.05 / close
            }
            tradeAmount = _N(tradeAmount, amountPrecision)

実はとても簡単で、金額を価格で割るだけです。

準備率の設計

リスク管理として常に一定額の資金をアカウントに確保したい場合は、このシンプルなメカニズムを設計できます。

            var balance = acc.Balance
            if (balance - initAcc.Equity * reserve < tradeAmount * close) {
                continue 
            }

視覚化チャート

実際の市場を運営する場合、口座残高、戦略ステータス、戦略ポジション、注文情報、市場チャートなどの戦略を観察することが必須です。これらは次のように設計されます。

        if (isShowPlot) {
            r.forEach(function(bar, index) {
                c.begin(bar)
                for (var i in atrs) {
                    var arr = atrs[i]
                    var up = arr[0] + arr[2][index] * arr[1]
                    var mid = arr[0]
                    var down = arr[0] - arr[2][index] * arr[1]
                    c.plot(up, 'up_' + (i + 1))
                    c.plot(mid, 'mid_' + (i + 1))
                    c.plot(down, 'down_' + (i + 1))
                }

                for (var signal of arrSignal) {
                    if (signal[0] == bar.Time) {
                        c.signal(signal[1], signal[2], signal[3])
                    }
                }

                c.close()
            })
        }

        // ...

        var orderTbl = {"type": "table", "title": "order", "cols": ["symbol", "type", "ratio", "price", "amount"], "rows": []}
        for (var i = arrUp.length - 1; i >= 0; i--) {
            var order = arrUp[i]
            orderTbl["rows"].push([order["symbol"], "short", order["ratio"], order["price"], order["amount"]])
        }
        for (var i = 0; i < arrDown.length; i++) {
            var order = arrDown[i]
            orderTbl["rows"].push([order["symbol"], "long", order["ratio"], order["price"], order["amount"]])
        }

        var posTbl = {"type": "table", "title": "pos", "cols": ["symbol", "type", "price", "amount"], "rows": []}
        for (var i = 0; i < pos.length; i++) {
            var p = pos[i]
            posTbl["rows"].push([p.Symbol, p.Type == PD_LONG ? "long" : "short", p.Price, p.Amount])
        }

        LogStatus(_D(), "初始权益:" + initAcc.Equity, ", 当前权益:" + acc.Equity, ", 运行状态:" + (isPaused ? "暂停交易" : "运行中"), 
            "\n`" + JSON.stringify(orderTbl) + "`\n", "`" + JSON.stringify(posTbl) + "`")

最終的には、200 行を超えるコードで、バックテストして実際の取引に実装できる完全な戦略が実装されました。私たちは究極の目標を達成しました。それは、「視覚化 + インタラクション + 自動化」を組み合わせた FMZ 上のオールインワンの定量取引システムを作成することです。

戦略運用効果とバックテスト結果

バックテストは参考用です。定量取引を行う人は、「バックテスト」では実際のシナリオを 100% シミュレートできないことを知っています。バックテストの主な目的は、戦略のロジックをテストし、戦略の堅牢性をテストし、基本的な機能をテストすることです。

定量的な旅はFMZから始まる

定量的な旅はFMZから始まる

戦略コード、パラメータ設計

パラメータ設計:

定量的な旅はFMZから始まる

インタラクションデザイン:

定量的な旅はFMZから始まる

戦略ソースコード:

/*backtest
start: 2024-04-27 18:40:00
end: 2025-04-10 00:00:00
period: 15m
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT","balance":100}]
*/

var atrPeriod = 20
var arrUp = null 
var arrDown = null 
var arrSignal = []

function calcProfit() {
    var initAcc = _G("initAcc")
    var nowAcc = _C(exchange.GetAccount)
    var profit = nowAcc.Equity - initAcc.Equity
    return profit
}

function clear(positions, r) {
    var close = r[r.length - 1].Close
    for (var p of positions) {
        if (p.Type == PD_LONG) {
            var id = exchange.CreateOrder(symbol, "closebuy", -1, p.Amount)
            if (!id) {
                Log("下单失败")
                continue 
            }
            arrSignal.push([r[r.length - 1].Time, "closelong", close, p.Amount])
            Log([r[r.length - 1].Time, "closelong", close, p.Amount], "@")
        } else if (p.Type == PD_SHORT) {
            var id = exchange.CreateOrder(symbol, "closesell", -1, p.Amount)
            if (!id) {
                Log("下单失败")
                continue 
            }
            arrSignal.push([r[r.length - 1].Time, "closeshort", close, p.Amount])
            Log([r[r.length - 1].Time, "closeshort", close, p.Amount], "@")
        }
    }
    arrUp = []
    arrDown = []
    _G("arrUp", arrUp)
    _G("arrDown", arrDown)
    var profit = calcProfit()
    LogProfit(profit)
}

function main() {
    var symbolInfo = symbol.split(".")
    if (symbolInfo.length != 2) {
        throw "error symbol:" + symbol
    } else {
        exchange.SetCurrency(symbolInfo[0])
        exchange.SetContractType(symbolInfo[1])
    }

    exchange.SetPrecision(pricePrecision, amountPrecision)

    let c = KLineChart({
        overlay: true
    }) 

    if (isReset) {
        _G(null)
        LogProfitReset()
        LogReset(1)
        c.reset()
    }

    arrUp = _G("arrUp")
    if (!arrUp) {
        arrUp = []
        _G("arrUp", arrUp)
    }

    arrDown = _G("arrDown")
    if (!arrDown) {
        arrDown = []
        _G("arrDown", arrDown)
    }

    var initAcc = _G("initAcc")
    if (!initAcc) {
        initAcc = _C(exchange.GetAccount)
        _G("initAcc", initAcc)
    }

    var isPaused = false     
    while (true) {
        var atrs = []        
        var r = _C(exchange.GetRecords, symbol)
        var pos = _C(exchange.GetPositions, symbol)
        var acc = _C(exchange.GetAccount)
        var open = r[r.length - 1].Open
        var close = r[r.length - 1].Close
        var atr = TA.ATR(r, atrPeriod)
        
        for (var i = 0; i < maxRatio; i++) {                        
            var up = open + atr[atr.length - 1] * (i + 1)
            var mid = open
            var down = open - atr[atr.length - 1] * (i + 1)
            atrs.push([open, (i + 1), atr])
            
            var tradeAmount = baseAmount * Math.pow(2, i)
            if (isAmountForUSDT) {
                tradeAmount = tradeAmount * 1.05 / close
            }
            tradeAmount = _N(tradeAmount, amountPrecision)

            var balance = acc.Balance
            if (balance - initAcc.Equity * reserve < tradeAmount * close) {
                continue 
            }

            if (close > up && i >= arrUp.length && !isPaused) {
                var id = exchange.CreateOrder(symbol, "sell", -1, tradeAmount)
                if (!id) {
                    Log("下单失败")
                    continue
                }
                arrUp.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
                _G("arrUp", arrUp)
                arrSignal.push([r[r.length - 1].Time, "short", close, tradeAmount])
                Log([r[r.length - 1].Time, "short", close, tradeAmount], "@")
            } else if (close < down && i >= arrDown.length && !isPaused) {
                var id = exchange.CreateOrder(symbol, "buy", -1, tradeAmount)
                if (!id) {
                    Log("下单失败")
                    continue 
                }
                arrDown.push({"symbol": symbol, "ratio": (i + 1), "amount": tradeAmount, "price": close})
                _G("arrDown", arrDown)
                arrSignal.push([r[r.length - 1].Time, "long", close, tradeAmount])
                Log([r[r.length - 1].Time, "long", close, tradeAmount], "@")
            } else if (((arrUp.length > 0 && close < mid) || (arrDown.length > 0 && close > mid)) && !isPaused) {
                clear(pos, r)
            }
        }

        if (isShowPlot) {
            r.forEach(function(bar, index) {
                c.begin(bar)
                for (var i in atrs) {
                    var arr = atrs[i]
                    var up = arr[0] + arr[2][index] * arr[1]
                    var mid = arr[0]
                    var down = arr[0] - arr[2][index] * arr[1]
                    c.plot(up, 'up_' + (i + 1))
                    c.plot(mid, 'mid_' + (i + 1))
                    c.plot(down, 'down_' + (i + 1))
                }

                for (var signal of arrSignal) {
                    if (signal[0] == bar.Time) {
                        c.signal(signal[1], signal[2], signal[3])
                    }
                }

                c.close()
            })
        }

        var cmd = GetCommand()
        if (cmd) {
            Log("交互指令:", cmd)
            var arrCmd = cmd.split(":")
            if (arrCmd.length == 2) {
                var strCmd = arrCmd[0]
                var param = parseFloat(arrCmd[1])
                if (strCmd == "atrPeriod") {
                    atrPeriod = param
                    Log("修改ATR参数:", atrPeriod)
                }
            } else {
                if (cmd == "isPaused" && !isPaused) {
                    isPaused = true
                    Log("暂停交易")
                } else if (cmd == "isPaused" && isPaused) {
                    isPaused = false 
                    Log("取消暂停交易")
                } else if (cmd == "clearAndPaused") {
                    clear(pos, r)
                    isPaused = true
                    Log("清仓、暂停交易")
                }
            }
        }

        var orderTbl = {"type": "table", "title": "order", "cols": ["symbol", "type", "ratio", "price", "amount"], "rows": []}
        for (var i = arrUp.length - 1; i >= 0; i--) {
            var order = arrUp[i]
            orderTbl["rows"].push([order["symbol"], "short", order["ratio"], order["price"], order["amount"]])
        }
        for (var i = 0; i < arrDown.length; i++) {
            var order = arrDown[i]
            orderTbl["rows"].push([order["symbol"], "long", order["ratio"], order["price"], order["amount"]])
        }

        var posTbl = {"type": "table", "title": "pos", "cols": ["symbol", "type", "price", "amount"], "rows": []}
        for (var i = 0; i < pos.length; i++) {
            var p = pos[i]
            posTbl["rows"].push([p.Symbol, p.Type == PD_LONG ? "long" : "short", p.Price, p.Amount])
        }

        LogStatus(_D(), "初始权益:" + initAcc.Equity, ", 当前权益:" + acc.Equity, ", 运行状态:" + (isPaused ? "暂停交易" : "运行中"), 
            "\n`" + JSON.stringify(orderTbl) + "`\n", "`" + JSON.stringify(posTbl) + "`")
        Sleep(5000)
    }
}

この戦略は教育目的のみです。実際の取引に使用でき、現在は利益も出ていますが、長期的な有効性をテストするには時間がかかります。戦略描画部分にはまだ最適化の余地があり、これにより一部の繰り返し操作を回避し、プログラム効率を向上させることができます。戦略ロジックをさらに最適化することもできます。

実際の取引は長い旅です

定量的な旅はFMZから始まる

GPT からの詩的な要約:

実際の取引は長い旅です。いつ帰ってきても、求めるのは心の平安だけ。ポジションを開くたびに、広大な市場に希望の光が蒔かれます。損切りするたびに、風雨の中でもよりしっかりと前進することを学びます。市場は潮の満ち引き​​のようなもので、利益と損失は夢のようなもの。私たちは数字の波の頂上で踊り、戦略の灯台の下で見守ります。あなたと私が、この長い旅路の中で、道に迷うことも孤独を恐れることもなく、最終的に私たち自身の光にたどり着くことができますように。

概要: 戦略策定からシステム思考へ

この記事では、完全な戦略を紹介するだけでなく、さらに重要なことに、「体系的な」戦略開発のアイデアを紹介します。戦略設計、ステータス管理、リスク管理、チャート操作から実際の実装まで、繰り返し再利用できるテンプレートのセットであり、定量取引を専門化に向けて進める唯一の方法でもあります。

FMZ プラットフォームを使用して独自の自動取引システムを構築し、シグナルを見逃さないようにしていただければ幸いです。

ご愛読、応援ありがとうございます。この戦略は教育目的のみに使用されます。実際の取引では注意してご利用ください。