Type/to search
3
Follow
1503
Followers
策略编写初级手册
Tutorials
Created 2019-08-13 17:47:27  Updated 2026-01-27 17:22:09
 33
 71689

欢迎来到FMZ量化交易平台!这份手册将带你从零开始,系统学习如何在FMZ平台上编写量化交易策略。无论你是编程新手还是有一定基础的开发者,这份手册都将帮助你快速上手,并最终能够独立编写简单的交易策略。


一、开篇导读

1.1 什么是FMZ量化平台

FMZ(发明者量化)是一个专业的量化交易平台,为用户提供策略编写、回测、模拟盘和实盘交易等一站式服务。平台支持连接全球主流加密货币交易所,让你可以用代码实现自动化交易。

1.2 平台支持的编程语言

FMZ平台支持多种编程语言编写策略:

语言特点适合人群
JavaScript语法简洁、易上手、运行效率高推荐新手首选
Python生态丰富、数据分析能力强有Python基础的用户
C++执行速度最快对性能有极致要求的用户
PINE语言兼容TradingView Pine Script,轻巧而功能强大熟悉TradingView的用户

本手册说明:为了让你最快速地入门,本手册以 JavaScript 为例进行讲解。JavaScript 语法简单直观,非常适合初学者。如果你使用其他语言,核心概念和API函数都是相同的,只需参考语法手册中对应语言的示例即可。

1.3 学习路径

理解基本结构 → 熟悉常用API → 编写简单策略 → 回测验证 → 模拟盘运行 → 实盘交易

重要提醒:在你完全熟悉平台和策略之前,请务必先在回测系统和模拟盘中充分测试,不要急于实盘交易!


二、策略编写环境

2.1 在线策略编辑器

FMZ提供了功能完善的在线策略编辑器,无需安装任何软件,打开浏览器即可开始编写策略。

进入编辑器的步骤

  1. 登录 FMZ官网
  2. 点击顶部导航栏的「策略库」
  3. 点击「新建策略」按钮
  4. 选择编程语言(推荐JavaScript)
  5. 开始编写你的第一个策略

编辑器主要功能

  • 代码高亮和自动补全
  • 实时语法检查
  • 策略参数配置面板
  • 交互控件设置区域
  • 一键回测功能

2.2 策略创建与保存

创建策略后,你可以:

  • 保存策略:点击「保存」按钮,策略会保存在云端
  • 版本管理:每次保存都会生成历史版本,方便回溯
  • 策略分类:可以为策略添加标签,便于管理

2.3 调试工具

FMZ提供了三种主要的调试方式:

调试方式说明使用场景
调试工具快速测试代码片段,查看API返回值验证单个函数的行为
回测系统用历史数据模拟策略运行验证策略逻辑、评估收益
模拟盘连接交易所的模拟账户实时运行验证策略在真实环境中的表现

三、策略基本结构

3.1 main函数:策略的入口

每个FMZ策略都必须有一个 main() 函数,这是策略的入口点。当策略启动时,系统会自动调用这个函数。

javascript
function main() { // 你的策略代码写在这里 Log("Hello, FMZ!") }

3.2 轮询架构:策略的核心模式

量化策略通常需要持续监控市场,因此大多数策略采用「轮询」架构——在一个无限循环中不断获取行情、判断条件、执行交易。

javascript
function main() { while (true) { // 无限循环 var ticker = exchange.GetTicker() // 获取行情 Log("当前价格:", ticker.Last) // 输出信息 Sleep(1000) // 休眠1秒(1000毫秒) } }

关键点Sleep() 函数非常重要!它控制每次轮询的间隔时间,避免过于频繁地请求交易所接口。建议至少设置1000毫秒(1秒)以上。

3.3 策略生命周期

FMZ策略有完整的生命周期管理,你可以通过特定函数在不同阶段执行代码:

函数触发时机用途
init()策略启动时,main()执行前初始化变量、设置参数
main()策略主体核心交易逻辑
onexit()策略正常停止时清理工作、撤销未完成订单
onerror()策略异常时(仅JS支持)错误处理

完整的生命周期示例

javascript
// 初始化函数(可选) function init() { Log("策略初始化完成") } // 主函数(必须) function main() { Log("策略开始运行") while (true) { // 你的交易逻辑 Sleep(1000) } } // 退出清理函数(可选) function onexit() { Log("策略停止,执行清理工作") }

3.4 全局变量与局部变量

在编写策略时,合理使用变量非常重要:

javascript
// 全局变量:在函数外部定义,整个策略都可以访问 var totalProfit = 0 var tradeCount = 0 function main() { while (true) { // 局部变量:在函数内部定义,只在当前作用域有效 var ticker = exchange.GetTicker() var currentPrice = ticker.Last // 全局变量可以在循环中持续累加 tradeCount++ LogStatus("交易次数:", tradeCount, "当前价格:", currentPrice) Sleep(1000) } }

四、策略参数系统(重点)

4.1 什么是策略参数

「策略参数」是让你的策略变得灵活可配置的关键特性。通过参数化,你可以:

  • 在不修改代码的情况下调整策略行为
  • 方便地进行参数优化和回测对比
  • 让策略适应不同的市场环境

没有参数化 vs 参数化的对比

javascript
// ❌ 硬编码(不灵活) function main() { var amount = 0.01 // 交易数量写死在代码里 var stopLoss = 0.05 // 止损比例写死在代码里 // ... } // ✅ 参数化(灵活) // 在界面上设置参数:Amount = 0.01, StopLoss = 0.05 function main() { Log("交易数量:", Amount) // Amount 是界面参数,可随时调整 Log("止损比例:", StopLoss) // StopLoss 是界面参数 // ... }

4.2 参数类型详解

在策略编辑页面下方的「策略参数」区域,你可以添加不同类型的参数:

类型说明代码中的值类型使用场景
数字型(number)数值输入框数字交易数量、价格、周期等
字符串(string)文本输入框字符串交易对名称、备注等
布尔型(true/false)开关控件true/false功能开关
下拉框(selected)选项列表索引值或绑定值选择模式、方向等
加密串加密输入框字符串API密钥等敏感信息

4.3 参数定义与使用

步骤1:在界面添加参数

在策略编辑器下方的「策略参数」表格中添加一行:

  • 变量:TradeAmount(这将成为代码中的全局变量名)
  • 描述:交易数量(显示给用户看的名称)
  • 类型:数字型(number)
  • 默认值:0.01

步骤2:在代码中直接使用

javascript
// TradeAmount 在界面设置后,自动成为全局变量 function main() { Log("设置的交易数量是:", TradeAmount) // 直接使用参数进行交易 var ticker = exchange.GetTicker() exchange.Buy(ticker.Sell, TradeAmount) }

4.4 下拉框参数的使用

下拉框参数特别适合需要在几个选项中选择的场景:

界面设置

  • 变量:Direction
  • 描述:交易方向
  • 类型:下拉框(selected)
  • 默认值:做多|做空|双向(用 | 分隔选项)

代码使用

javascript
function main() { // Direction 的值是选项的索引:0=做多, 1=做空, 2=双向 if (Direction == 0) { Log("当前设置为做多模式") } else if (Direction == 1) { Log("当前设置为做空模式") } else { Log("当前设置为双向模式") } }

4.5 参数分组

当策略参数较多时,可以使用「组件配置」中的「分组」功能,将相关参数归类显示,使界面更加清晰。

4.6 实际示例:可配置的网格交易参数

javascript
/* 界面参数设置: - Symbol (字符串): BTC_USDT // 交易对 - GridCount (数字): 10 // 网格数量 - GridSpacing (数字): 100 // 网格间距(USDT) - AmountPerGrid (数字): 0.001 // 每格交易量 - EnableBuy (布尔): true // 是否开启买入 - EnableSell (布尔): true // 是否开启卖出 */ function main() { Log("=== 网格策略参数 ===") Log("交易对:", Symbol) Log("网格数量:", GridCount) Log("网格间距:", GridSpacing, "USDT") Log("每格交易量:", AmountPerGrid) Log("买入开关:", EnableBuy ? "开启" : "关闭") Log("卖出开关:", EnableSell ? "开启" : "关闭") // 根据参数执行策略逻辑... }

五、策略交互功能(重点)

5.1 什么是策略交互

「策略交互」让你可以在策略运行过程中与之"对话"——通过点击按钮或输入数据来触发特定操作。这对于以下场景非常有用:

  • 手动触发买入/卖出操作
  • 动态修改策略参数
  • 紧急情况下的干预操作
  • 查询特定信息

5.2 交互控件类型

在策略编辑器的「策略交互」区域,你可以添加以下类型的控件:

控件类型说明GetCommand()收到的消息格式
按钮(button)纯按钮,无输入控件名
数字型(number)按钮+数字输入框控件名:数值
字符串(string)按钮+文本输入框控件名:文本
布尔型(boolean)按钮+开关控件名:true/false
下拉框(selected)按钮+下拉选择控件名:选项索引

5.3 GetCommand()函数详解

GetCommand() 是接收交互命令的核心函数:

javascript
function main() { while (true) { // 获取交互命令 var cmd = GetCommand() // 如果有命令传入 if (cmd) { Log("收到交互命令:", cmd) } Sleep(1000) } }

返回值说明

  • 没有命令时返回空字符串 ""
  • 有命令时返回格式为 控件名:数据控件名(纯按钮)

5.4 解析交互命令

当交互控件带有输入数据时,需要解析命令:

javascript
function main() { while (true) { var cmd = GetCommand() if (cmd) { // 按冒号分割命令 var arr = cmd.split(":") var action = arr[0] // 控件名称 var value = arr[1] // 输入的值(如果有) if (action == "buy") { Log("执行买入,数量:", value) } else if (action == "sell") { Log("执行卖出,数量:", value) } } Sleep(500) } }

5.5 实际示例:手动交易控制

假设你在「策略交互」中添加了以下控件:

  • buy:数字型,描述为"买入"
  • sell:数字型,描述为"卖出"
  • cancelAll:按钮型,描述为"撤销所有订单"
javascript
function main() { while (true) { var cmd = GetCommand() if (cmd) { var arr = cmd.split(":") var action = arr[0] if (action == "buy" && arr[1]) { var amount = parseFloat(arr[1]) var ticker = exchange.GetTicker() Log("手动买入,价格:", ticker.Sell, "数量:", amount) exchange.Buy(ticker.Sell, amount) } else if (action == "sell" && arr[1]) { var amount = parseFloat(arr[1]) var ticker = exchange.GetTicker() Log("手动卖出,价格:", ticker.Buy, "数量:", amount) exchange.Sell(ticker.Buy, amount) } else if (action == "cancelAll") { Log("撤销所有未完成订单") var orders = exchange.GetOrders() for (var i = 0; i < orders.length; i++) { exchange.CancelOrder(orders[i].Id) } } } // 显示状态 var account = exchange.GetAccount() LogStatus("可用余额:", account.Balance, "可用币:", account.Stocks) Sleep(1000) } }

5.6 状态栏中的交互按钮

除了使用「策略交互」区域,你还可以在状态栏中动态创建交互按钮:

javascript
function main() { while (true) { // 创建状态栏按钮 var table = { type: "table", title: "操作面板", cols: ["操作", "动作"], rows: [ ["一键平仓", {"type": "button", "cmd": "closeAll", "name": "执行"}], ["紧急停止", {"type": "button", "cmd": "stop", "name": "停止", "class": "btn btn-xs btn-danger"}] ] } LogStatus("`" + JSON.stringify(table) + "`") var cmd = GetCommand() if (cmd == "closeAll") { Log("执行一键平仓") } else if (cmd == "stop") { Log("紧急停止") return // 退出策略 } Sleep(1000) } }

六、常用API函数详解

6.1 函数分类概览

分类主要函数用途
行情类GetTicker, GetDepth, GetRecords获取市场数据
交易类Buy, Sell, CancelOrder, GetOrders下单和订单管理
账户类GetAccount, GetPositions查询资产和持仓
工具类Log, Sleep, _N, _D日志、延时、格式化

6.2 行情类函数

关于symbol参数:最新版API中,行情类函数(GetTicker、GetDepth、GetRecords)、查询类函数(GetOrders、GetPositions)均支持传入 symbol 参数来直接指定要查询的品种,无需提前调用 SetContractType()。symbol格式示例:现货 "BTC_USDT",永续合约 "BTC_USDT.swap"。同时也兼容之前通过 SetContractType() 设置当前操作品种的方式。此外,平台还提供了 exchange.CreateOrder(symbol, side, price, amount) 函数,可以直接指定品种和方向下单。

exchange.GetTicker() - 获取实时行情

用途:获取当前交易对的最新行情数据,包括最新价、买一价、卖一价等。支持传入symbol参数指定品种。

返回值:Ticker结构

字段类型说明
Lastnumber最新成交价
Buynumber买一价
Sellnumber卖一价
Highnumber24小时最高价
Lownumber24小时最低价
Volumenumber24小时成交量
Timenumber时间戳(毫秒)

示例

javascript
function main() { // 不传参数:使用当前设置的交易对 var ticker = exchange.GetTicker() if (ticker) { Log("最新价:", ticker.Last) Log("买一价:", ticker.Buy) Log("卖一价:", ticker.Sell) Log("24H最高:", ticker.High) Log("24H最低:", ticker.Low) } // 传入symbol参数:直接指定品种 var tickerBTC = exchange.GetTicker("BTC_USDT") // 现货 var tickerSwap = exchange.GetTicker("BTC_USDT.swap") // 永续合约 }

exchange.GetDepth() - 获取深度数据

用途:获取订单簿数据,即买卖盘挂单情况。

返回值:Depth结构

字段类型说明
Asksarray卖单数组,按价格从低到高排序
Bidsarray买单数组,按价格从高到低排序
Timenumber时间戳

示例

javascript
function main() { var depth = exchange.GetDepth() if (depth) { Log("卖一价:", depth.Asks[0].Price, "卖一量:", depth.Asks[0].Amount) Log("买一价:", depth.Bids[0].Price, "买一量:", depth.Bids[0].Amount) } }

exchange.GetRecords() - 获取K线数据

用途:获取K线数据,用于技术分析和指标计算。

语法

javascript
exchange.GetRecords() // 使用默认周期 exchange.GetRecords(PERIOD_M5) // 指定5分钟周期 exchange.GetRecords("BTC_USDT", 60, 100) // 指定交易对、周期(秒)、数量

常用周期常量

常量周期
PERIOD_M11分钟
PERIOD_M55分钟
PERIOD_M1515分钟
PERIOD_M3030分钟
PERIOD_H11小时
PERIOD_D11天

返回值:Record数组,每个Record包含:

字段说明
TimeK线起始时间戳
Open开盘价
High最高价
Low最低价
Close收盘价
Volume成交量

示例

javascript
function main() { // 获取1小时K线 var records = exchange.GetRecords(PERIOD_H1) if (records && records.length > 0) { var lastBar = records[records.length - 1] // 最新一根K线 Log("最新K线 - 开:", lastBar.Open, "高:", lastBar.High, "低:", lastBar.Low, "收:", lastBar.Close) Log("K线数量:", records.length) } }

6.3 交易类函数

exchange.Buy() - 买入下单

用途:下买单(现货买入/合约做多开仓)。

语法

javascript
exchange.Buy(price, amount) // 限价单 exchange.Buy(-1, amount) // 市价单(amount为金额)

参数

  • price:订单价格,-1表示市价单
  • amount:订单数量

返回值:成功返回订单ID,失败返回null

示例

javascript
function main() { // 限价买入:以50000价格买入0.01个 var id = exchange.Buy(50000, 0.01) Log("限价单ID:", id) // 市价买入:花100 USDT买入 var id2 = exchange.Buy(-1, 100) Log("市价单ID:", id2) }

exchange.Sell() - 卖出下单

用途:下卖单(现货卖出/合约做空开仓)。

语法

javascript
exchange.Sell(price, amount) // 限价单 exchange.Sell(-1, amount) // 市价单

示例

javascript
function main() { // 限价卖出:以60000价格卖出0.01个 var id = exchange.Sell(60000, 0.01) Log("订单ID:", id) // 市价卖出:卖出0.01个币 var id2 = exchange.Sell(-1, 0.01) Log("市价单ID:", id2) }

exchange.CreateOrder() - 指定品种下单

用途:直接指定交易品种和方向下单,无需提前调用 SetContractType()SetDirection()

语法

javascript
exchange.CreateOrder(symbol, side, price, amount)

参数

  • symbol:交易品种,如 "BTC_USDT"(现货)、"BTC_USDT.swap"(永续合约)
  • side:交易方向,"buy"(买入/做多)、"sell"(卖出/做空)、"closebuy"(平多)、"closesell"(平空)
  • price:订单价格,-1表示市价单
  • amount:订单数量

返回值:成功返回订单ID,失败返回null

示例

javascript
function main() { // 现货买入 var id1 = exchange.CreateOrder("BTC_USDT", "buy", -1, 100) // 市价花100 USDT买入BTC Log("现货买单ID:", id1) // 永续合约做多 var id2 = exchange.CreateOrder("BTC_USDT.swap", "buy", 50000, 1) // 限价做多 Log("合约多单ID:", id2) // 平仓 var id3 = exchange.CreateOrder("BTC_USDT.swap", "closebuy", -1, 1) // 市价平多 Log("平仓单ID:", id3) }

提示CreateOrder 是更灵活的下单方式,特别适合同时操作多个品种的策略。Buy()Sell() 函数依然可用,但它们不支持symbol参数,需要配合 SetContractType() 使用。


exchange.CancelOrder() - 撤销订单

用途:取消指定的未完成订单。

语法

javascript
exchange.CancelOrder(orderId)

返回值:true表示撤单请求成功,false表示失败

示例

javascript
function main() { // 下一个远离当前价格的限价单(不会立即成交) var id = exchange.Buy(1000, 0.01) Log("下单成功,订单ID:", id) Sleep(2000) // 等待2秒 // 撤销订单 var ret = exchange.CancelOrder(id) Log("撤单结果:", ret ? "成功" : "失败") }

exchange.GetOrders() - 获取未完成订单

用途:查询当前未成交的挂单。

返回值:Order数组,无挂单时返回空数组 []

Order结构

字段说明
Id订单ID
Price下单价格
Amount下单数量
DealAmount已成交数量
Type订单类型(0买单/1卖单)
Status订单状态

示例

javascript
function main() { var orders = exchange.GetOrders() if (orders.length == 0) { Log("当前没有未完成的订单") } else { Log("未完成订单数量:", orders.length) for (var i = 0; i < orders.length; i++) { Log("订单", i+1, "- ID:", orders[i].Id, "价格:", orders[i].Price, "数量:", orders[i].Amount) } } }

6.4 账户类函数

exchange.GetAccount() - 获取账户信息

用途:查询当前账户的资产余额。

返回值:Account结构

字段说明
Balance计价币余额(如USDT)
FrozenBalance冻结的计价币
Stocks交易币余额(如BTC)
FrozenStocks冻结的交易币

示例

javascript
function main() { var account = exchange.GetAccount() if (account) { Log("USDT余额:", account.Balance) Log("USDT冻结:", account.FrozenBalance) Log("BTC余额:", account.Stocks) Log("BTC冻结:", account.FrozenStocks) } }

exchange.GetPositions() - 获取持仓(合约)

用途:查询合约的持仓信息。

返回值:Position数组

字段说明
Symbol合约代码
Amount持仓数量
Price持仓均价
Profit未实现盈亏
Type持仓方向(0多头/1空头)
Margin保证金

示例

javascript
function main() { // 方式一:通过symbol参数直接指定品种(推荐) var positions = exchange.GetPositions("BTC_USDT.swap") // 方式二:先设置合约类型,再查询(兼容旧写法) // exchange.SetContractType("swap") // var positions = exchange.GetPositions() if (positions.length == 0) { Log("当前没有持仓") } else { for (var i = 0; i < positions.length; i++) { var pos = positions[i] Log("持仓:", pos.Symbol, "方向:", pos.Type == 0 ? "多" : "空", "数量:", pos.Amount, "盈亏:", pos.Profit) } } }

6.5 工具类函数

Log() - 输出日志

用途:在日志区域输出信息,支持多参数。

特殊用法

  • @ 结尾:触发消息推送
  • #ff0000 等颜色代码:设置文字颜色
javascript
function main() { Log("普通日志") Log("红色警告", "#ff0000") Log("推送消息@") // 会推送到你的邮箱/微信等 Log("多个参数:", 123, true, {a: 1}) }

LogStatus() - 更新状态栏

用途:在状态栏显示信息,不写入日志数据库,适合显示实时数据。

javascript
function main() { while (true) { var ticker = exchange.GetTicker() // 状态栏实时更新,不会产生大量日志 LogStatus("当前价格:", ticker.Last, "\n更新时间:", _D()) Sleep(1000) } }

LogProfit() - 记录收益

用途:记录策略收益,并绘制收益曲线。

javascript
function main() { var initBalance = 10000 while (true) { var account = exchange.GetAccount() var profit = account.Balance - initBalance LogProfit(profit) // 记录收益,绘制曲线 Sleep(60000) // 每分钟记录一次 } }

Sleep() - 休眠等待

用途:让程序暂停指定的毫秒数。

javascript
Sleep(1000) // 休眠1秒 Sleep(500) // 休眠0.5秒 Sleep(60000) // 休眠1分钟

重要:在轮询循环中必须使用Sleep(),否则会因为请求过于频繁而被交易所限制。


_N() - 数值精度格式化

用途:格式化浮点数,控制小数位数。

javascript
var num = 3.1415926 Log(_N(num, 2)) // 输出: 3.14 Log(_N(num, 4)) // 输出: 3.1415 // 也可以处理整数部分 Log(_N(1234, -2)) // 输出: 1200

_D() - 时间格式化

用途:将时间戳转换为可读的日期时间字符串。

javascript
Log(_D()) // 输出当前时间,如:2024-01-15 10:30:45 Log(_D(1705285845000)) // 将时间戳转换为日期字符串

6.6 技术指标函数

FMZ内置了常用的技术指标计算函数(TA库):

函数指标名称
TA.MA(records, period)移动平均线
TA.EMA(records, period)指数移动平均线
TA.MACD(records, fast, slow, signal)MACD指标
TA.RSI(records, period)RSI相对强弱指标
TA.KDJ(records, n, k, d)KDJ随机指标
TA.BOLL(records, period, multiplier)布林带

示例:计算均线

javascript
function main() { var records = exchange.GetRecords(PERIOD_H1) if (records && records.length > 20) { var ma5 = TA.MA(records, 5) // 5周期均线 var ma20 = TA.MA(records, 20) // 20周期均线 // 获取最新的均线值 var currentMA5 = ma5[ma5.length - 1] var currentMA20 = ma20[ma20.length - 1] Log("MA5:", currentMA5, "MA20:", currentMA20) } }

七、完整策略示例

7.1 示例一:行情监控策略(入门级)

这是最简单的策略,功能是定时获取并显示行情数据。

javascript
/* 策略名称:行情监控策略 功能说明:每3秒获取一次行情,在状态栏显示关键数据 适合学习:基本结构、API调用、状态栏使用 */ function main() { Log("行情监控策略启动") while (true) { // 获取行情数据 var ticker = exchange.GetTicker() // 检查是否获取成功 if (ticker) { // 构建状态信息 var info = "=== 实时行情 ===" + "\n" info += "最新价格: " + ticker.Last + "\n" info += "买一价格: " + ticker.Buy + "\n" info += "卖一价格: " + ticker.Sell + "\n" info += "24H最高: " + ticker.High + "\n" info += "24H最低: " + ticker.Low + "\n" info += "24H成交量: " + ticker.Volume + "\n" info += "更新时间: " + _D() // 更新状态栏 LogStatus(info) } else { LogStatus("获取行情失败,等待重试...") } // 休眠3秒 Sleep(3000) } }

运行效果:策略启动后,状态栏会每3秒更新一次,显示当前的行情数据。

可修改优化

  • 修改Sleep的时间调整刷新频率
  • 添加更多数据显示,如深度数据
  • 添加价格变化提醒

7.2 示例二:简单均线策略(理解交易逻辑)

这个策略实现了经典的双均线交叉策略。

javascript
/* 策略名称:双均线交叉策略 功能说明:短期均线上穿长期均线时买入,下穿时卖出 适合学习:K线数据、指标计算、交易逻辑、持仓判断 界面参数设置: - FastPeriod (数字): 5 // 短期均线周期 - SlowPeriod (数字): 20 // 长期均线周期 - TradeAmount (数字): 0.01 // 每次交易数量 */ // 全局变量:记录上一次的均线状态 var lastCrossState = 0 // 0=未知, 1=金叉, -1=死叉 function main() { Log("双均线策略启动") Log("短期均线:", FastPeriod, "长期均线:", SlowPeriod) while (true) { // 获取K线数据 var records = exchange.GetRecords(PERIOD_H1) // 检查K线数量是否足够计算均线 if (!records || records.length < SlowPeriod + 1) { LogStatus("等待K线数据... 当前:", records ? records.length : 0, "根") Sleep(1000) continue } // 计算均线 var maFast = TA.MA(records, FastPeriod) var maSlow = TA.MA(records, SlowPeriod) // 获取最新和前一个均线值(用于判断交叉) var currentFast = maFast[maFast.length - 2] // 使用倒数第二个(已完成的K线) var currentSlow = maSlow[maSlow.length - 2] var prevFast = maFast[maFast.length - 3] var prevSlow = maSlow[maSlow.length - 3] // 判断均线交叉 var crossState = 0 if (prevFast <= prevSlow && currentFast > currentSlow) { crossState = 1 // 金叉:短期均线上穿长期均线 } else if (prevFast >= prevSlow && currentFast < currentSlow) { crossState = -1 // 死叉:短期均线下穿长期均线 } // 获取账户信息 var account = exchange.GetAccount() var ticker = exchange.GetTicker() // 交易逻辑 if (crossState == 1 && lastCrossState != 1) { // 金叉买入 Log("检测到金叉,执行买入") exchange.Buy(-1, TradeAmount * ticker.Last) // 市价买入 lastCrossState = 1 } else if (crossState == -1 && lastCrossState != -1) { // 死叉卖出 if (account.Stocks >= TradeAmount) { Log("检测到死叉,执行卖出") exchange.Sell(-1, TradeAmount) // 市价卖出 } lastCrossState = -1 } // 更新状态栏 var status = "=== 双均线策略 ===" + "\n" status += "当前价格: " + ticker.Last + "\n" status += "快线(MA" + FastPeriod + "): " + _N(currentFast, 2) + "\n" status += "慢线(MA" + SlowPeriod + "): " + _N(currentSlow, 2) + "\n" status += "信号状态: " + (lastCrossState == 1 ? "金叉(持有)" : lastCrossState == -1 ? "死叉(空仓)" : "等待") + "\n" status += "账户余额: " + _N(account.Balance, 2) + " USDT" + "\n" status += "持有数量: " + _N(account.Stocks, 6) + "\n" status += "更新时间: " + _D() LogStatus(status) Sleep(5000) // 5秒检查一次 } }

核心逻辑说明

  1. 获取K线数据,计算两条均线
  2. 判断均线是否发生交叉(比较当前和上一周期的相对位置)
  3. 金叉时买入,死叉时卖出
  4. 使用lastCrossState避免重复下单

可修改优化

  • 调整均线周期参数
  • 添加止损止盈逻辑
  • 改为合约交易,支持做空

7.3 示例三:带参数和交互的完整策略(综合应用)

这个策略综合运用了参数、交互、状态栏表格等功能。

javascript
/* 策略名称:网格交易策略(演示版) 功能说明:在指定价格范围内设置网格,低买高卖 适合学习:参数系统、交互控件、状态栏表格、完整交易逻辑 界面参数设置: - BasePrice (数字): 0 // 基准价格,0表示使用当前价 - GridCount (数字): 5 // 单边网格数量 - GridSpacing (数字): 100 // 网格间距(USDT) - AmountPerGrid (数字): 0.001 // 每格交易量 交互控件设置: - start: 按钮型, 描述"开始运行" - stop: 按钮型, 描述"停止运行" - setBase: 数字型, 描述"设置基准价" */ // 全局变量 var isRunning = false var gridOrders = [] // 存储网格订单信息 function init() { Log("网格策略初始化") Log("网格数量:", GridCount, "网格间距:", GridSpacing, "每格数量:", AmountPerGrid) } // 计算网格价格 function calculateGridPrices(basePrice) { var prices = [] for (var i = -GridCount; i <= GridCount; i++) { if (i != 0) { prices.push({ price: basePrice + i * GridSpacing, side: i < 0 ? "buy" : "sell", level: Math.abs(i) }) } } return prices } // 显示状态表格 function showStatus(ticker, account) { var table = { type: "table", title: "网格状态", cols: ["项目", "数值"], rows: [ ["运行状态", isRunning ? "运行中 #00ff00" : "已停止 #ff0000"], ["当前价格", ticker.Last], ["基准价格", BasePrice > 0 ? BasePrice : "未设置"], ["账户余额", _N(account.Balance, 2) + " USDT"], ["持有数量", _N(account.Stocks, 6)], ["网格数量", GridCount + " x 2"], ["网格间距", GridSpacing + " USDT"], ["更新时间", _D()] ] } // 添加控制按钮 var btnStart = {"type": "button", "cmd": "start", "name": "开始"} var btnStop = {"type": "button", "cmd": "stop", "name": "停止", "class": "btn btn-xs btn-danger"} var controlTable = { type: "table", title: "手动控制", cols: ["启动", "停止"], rows: [[btnStart, btnStop]] } LogStatus("`" + JSON.stringify([table, controlTable]) + "`") } function main() { while (true) { // 处理交互命令 var cmd = GetCommand() if (cmd) { if (cmd == "start") { if (BasePrice <= 0) { var t = exchange.GetTicker() BasePrice = t.Last Log("使用当前价格作为基准:", BasePrice) } isRunning = true Log("网格策略开始运行,基准价格:", BasePrice) } else if (cmd == "stop") { isRunning = false Log("网格策略停止运行") } else if (cmd.indexOf("setBase:") == 0) { var newBase = parseFloat(cmd.split(":")[1]) if (newBase > 0) { BasePrice = newBase Log("基准价格已更新为:", BasePrice) } } } // 获取行情和账户 var ticker = exchange.GetTicker() var account = exchange.GetAccount() if (!ticker || !account) { Sleep(1000) continue } // 如果策略在运行中,检查网格逻辑 if (isRunning && BasePrice > 0) { var gridPrices = calculateGridPrices(BasePrice) // 这里简化处理,实际策略需要更复杂的订单管理 for (var i = 0; i < gridPrices.length; i++) { var grid = gridPrices[i] // 检查是否触发网格 if (grid.side == "buy" && ticker.Last <= grid.price) { // 价格下跌到买入网格 Log("触发买入网格,价格:", grid.price, "层级:", grid.level) // exchange.Buy(grid.price, AmountPerGrid) // 实际交易时取消注释 } else if (grid.side == "sell" && ticker.Last >= grid.price) { // 价格上涨到卖出网格 Log("触发卖出网格,价格:", grid.price, "层级:", grid.level) // exchange.Sell(grid.price, AmountPerGrid) // 实际交易时取消注释 } } } // 更新状态显示 showStatus(ticker, account) Sleep(2000) } } function onexit() { Log("策略退出,执行清理...") // 可以在这里撤销所有未完成订单 }

功能亮点

  1. 参数系统:网格数量、间距、交易量都可在界面配置
  2. 交互控件:可以手动启动/停止策略,动态设置基准价
  3. 状态栏表格:清晰展示策略运行状态和账户信息
  4. 生命周期管理:init()初始化,onexit()清理

可修改优化

  • 添加订单跟踪,避免重复下单
  • 添加动态网格,跟随价格移动
  • 添加总体盈亏统计

八、调试与常见问题

8.1 如何查看日志排查问题

FMZ的日志系统是你调试的好帮手:

日志级别

  • Log() - 普通信息
  • Log("错误!", "#ff0000") - 红色警告
  • 系统自动记录的交易日志

调试技巧

javascript
function main() { // 打印关键变量的值 var ticker = exchange.GetTicker() Log("调试 - ticker对象:", JSON.stringify(ticker)) // 在关键位置打印执行到哪里了 Log("执行到第1步") // ... 一些代码 ... Log("执行到第2步") // 打印类型,排查数据问题 var value = "123" Log("value的类型:", typeof(value), "值:", value) }

8.2 常见报错及解决方法

报错信息原因解决方法
nullundefined 错误API返回空值加判断:if (ticker) { ... }
rate limit请求频率过高增加Sleep时间
insufficient balance余额不足检查账户余额,减少交易量
invalid order订单参数错误检查价格和数量是否合法
direction is buy, invalid order type Sell合约方向设置错误检查SetDirection是否正确

8.3 回测与模拟盘的使用建议

回测系统

  • 用于快速验证策略逻辑
  • 可以测试不同参数组合
  • 注意:回测结果不等于实盘表现,避免过度优化

模拟盘

  • 连接交易所的模拟账户
  • 真实的市场数据和撮合逻辑
  • 建议在模拟盘运行至少1-2周再考虑实盘

8.4 新手常犯的错误

  1. 忘记Sleep:导致请求过于频繁,被交易所限制
  2. 不检查返回值:API可能返回null,直接使用会报错
  3. 市价单金额/数量混淆:现货市价买单的amount是金额,卖单是数量
  4. 合约方向未设置:使用Buy/Sell下合约单时必须先SetContractType和SetDirection,推荐使用CreateOrder直接指定品种和方向
  5. 回测过度优化:参数调得太完美,实盘效果差
  6. 急于实盘:没有充分测试就上实盘,造成损失

九、进阶指引

9.1 后续学习路径

恭喜你完成了初级手册的学习!接下来你可以:

  1. 深入学习API:阅读完整的语法手册,了解更多函数
  2. 学习模板库:FMZ提供了很多实用的模板函数
  3. 研究策略广场:学习其他用户分享的策略思路
  4. 尝试合约交易:学习合约交易的特殊API
  5. 探索高级功能:多交易所、多线程、数据库等

9.2 推荐资源

9.3 从模拟到实盘的注意事项

当你准备好进行实盘交易时,请记住:

  1. 小资金起步:先用小资金测试,确认没问题再增加
  2. 设置风控:一定要有止损逻辑,防止意外损失
  3. 监控运行:定期检查策略运行状态
  4. 保持学习:市场在变化,策略也需要不断优化
  5. 分散风险:不要把所有资金放在一个策略上

结语

量化交易是一个需要不断学习和实践的领域。希望这份手册能帮助你迈出第一步。记住,耐心和谨慎是成功的关键——先在模拟环境中充分测试,积累经验后再考虑实盘交易。

如果你在学习过程中遇到问题,可以:

  • 查阅官方文档
  • 在社区论坛提问
  • 参考策略广场中的示例

祝你量化之路顺利!


本手册基于FMZ量化平台官方文档编写,内容以平台最新文档为准。

Related Recommendations
Comment
All comments (27)

    api

    3 years ago

    如何在本地实现策略运行呢?我写了一个简单的Log输出语句,并且按照文末的操作。
    第一步,先用一台笔记本作为服务器,运行托管者程序;
    第二步,写一个简单的Log输出信息的test.py程序(FMZ 的API接口函数);
    第三步,按文末那样,写个runfile,通过run.py调用test.py运行。 img

    4 years ago

    我买的网易云量化交易课程怎么没了,现在去哪里看

    5 years ago

    谢谢

    5 years ago

    many

    5 years ago

    hi

    5 years ago

    学习ing

    5 years ago

    有一个小的文字错误,GetAccount 获取账户 介绍中,FrozenStocks应该是冻结余额而不是可用余额吧

    5 years ago

    改了

    5 years ago

    有没有做BTB的实盘教程,

    a year ago

    大佬麻烦问下咱们有官方交流群吗?有时候遇到问题不知道该在哪提问

    5 years ago

    加首页微信,拉你入群

    5 years ago

    加我进群,我的实盘执行不起来

    a year ago

    getorder outtime 获取订单超时,okex的交易所,怎么办

    5 years ago

    再次获取

    5 years ago

    担保资产率获取不到吗,到0%会被强制平仓的担保资产率

    5 years ago

    原始信息里有,可以用GetRawJSON或者查看字段里的info信息

    5 years ago

    我是看1分钟k线图操作的,所以Python死循环的sleep time 可以设置为0.1s,也就是sleep(100)吗,我看你其中写过一个sleep(10),也就是0.1s不会超过huobi HM的API限制吗?

    6 years ago

    exchange.SetDirection("closebuy"); //如果是永续合约,直接设置exchange.SetDirection("sell")

    这儿我试了OKex的永续合约,如果设置成 sell,直接开空了,平不是平多

    6 years ago

    exchange.SetDirection("closebuy"); //如果是永续合约,直接设置exchange.SetDirection("sell")

    这儿我试了OKex的永续合约,如果设置成 sell,直接开空了,平不是平多

    6 years ago

    有些永续合约允许双向持仓的,需要设置平仓。我更新一下,原来只有bitmex

    6 years ago

    不错不错,还有管理回复。。我发现代码里好多拼写错误,哈哈

    6 years ago

    GetOrders 的代码里面有两个拼写错误。。。一个是 function写成了 fuction,另一个是for循环的条件里 ; 打成了 ,

    6 years ago

    嗯嗯,已改正,感谢指出错误

    6 years ago

    是我错了。。。
    exchange.Buy(-1, 0.5),交易对是ETH_BTC,市价单代表买入0.5BTC的ETH
    exchange.Buy(price, 0.5),如果是这种限价单,则代表用price的价格买入 0.5ETH

    6 years ago

    exchange.Buy(-1, 0.5),交易对是ETH_BTC,则代表市价买入0.5BTC的ETH

    这里应该是【代表市价买入0.5ETH】

    6 years ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)