4
Подписаться
1271
Подписчики

Количественное путешествие начинается с FMZ

Создано: 2025-04-18 09:31:42, Обновлено: 2025-04-26 11:50:01
comments   0
hits   937

Количественное путешествие начинается с FMZ

введение

Вы когда-нибудь думали, что можно легко приступить к количественной торговле и начать прямо сейчас, не проводя ночи напролет за написанием кода для создания фреймворка, проектированием пользовательского интерфейса и различных деталей и механизмов дизайна? Все становится возможным на количественной платформе FMZ. Вам не нужны ни глубокие познания в программировании, ни необходимость беспокоиться о сложных процессах развертывания — все, что вам нужно, это компьютер и учетная запись, чтобы начать свое «универсальное» количественное путешествие. Эта статья поможет вам с нуля быстро начать работу с FMZ, ощутить прелесть автоматизированной торговли и использовать данные и стратегии для овладения ритмом рынка. Независимо от того, новичок вы или опытный специалист, стремящийся повысить эффективность, вам стоит попробовать этот путь.

Замешательство новичков в количественной торговле

Я часто общаюсь и общаюсь с новичками платформы. Новички в количественной торговле обычно не понимают всего процесса проектирования. Когда у меня возникают торговые идеи, я часто не знаю, с чего начать, и чувствую себя подавленным.

Смущает следующее:

  • Как проектировать позиции открытия и закрытия
  • Как разработать расчет выручки
  • Как разработать стратегии для возобновления и продолжения развития торговли
  • Как разработать отображение стратегической диаграммы
  • Как разработать стратегический контроль взаимодействия

Давайте вместе разрешим эту путаницу.

Объяснение дизайна

В мире количественной торговли разработка стратегии часто представляет собой бесконечный исследовательский путь. Возможно, вы пытались писать индикаторы или пытались слепо следовать сигналам покупки и продажи, но по-настоящему многого могут добиться те стратегические системы, которые могут быть «видимыми, регулируемыми и стабильными». На основе количественной платформы FMZ вы можете получить практический опыт «хождения в ногу со временем». Создайте простую стратегию, от настройки параметров, отображения диаграмм до интерактивных функций и расчета прибылей и убытков, чтобы полностью удовлетворить требования к разработке стратегии.

Идея стратегии заключается в пошаговой стратегии увеличения позиции на основе ATR, пошаговой логике построения сетки позиций (длинные и короткие двунаправленные), адаптивном расчете волатильности ATR и логике ликвидации позиции (когда рынок разворачивается к центральной оси).

Эта стратегия основана на следующих требованиях к проектированию:

Добавляйте позиции и закрывайте позиции в соответствии с ценовыми прорывами на разных уровнях

Создайте два массива для управления постепенным увеличением позиций.

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], "@")  // 消息推送

Получать push-уведомления (приложение FMZ и другие приложения также будут получать push-уведомления):

Количественное путешествие начинается с FMZ

Статистика в реальном времени и отображение прибылей и позиций

Функция расчета прибыли и убытка вызывается каждый раз при закрытии позиции для расчета прибыли и убытка и вывода кривой прибыли и убытка.

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

Поддержка сохранения состояния (восстановление точки останова)

Использовать ФМЗ_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 для создания собственной автоматизированной торговой системы, которая позволит вам никогда не пропустить ни одного сигнала.

Спасибо за ваше чтение и поддержку. Стратегия предназначена только для учебных целей. Пожалуйста, используйте его с осторожностью в реальной торговле.