Type/to search
8
Follow
1376
Followers
Analisis ringkas kod Icebreaker (asal) Sila betulkan sebarang ralat.
FAQ
Created 2016-04-13 12:58:37  Updated 2018-10-29 12:23:16
 11
 10634

Berkongsi Pautan Strategi: https://www.fmz.com/strategy/9929

/*说明 改自 单点狙击高频加仓自动反手解套算法 V1.2 但是不反手,可以单方向做,加了止损. 效果很好, 识货的拿走,看源码. 不多说了. 回测时资金设置在10万以上, 币设置在30以上, 这样才能达到理想效果, 资金越大, 策略抗波动能力越强 */ /*参数 OpType 开仓方向 下拉框(selected) 做多|做空 OpAmount 开仓数量 数字型(number) 0.1 OpMode 开仓方式 下拉框(selected) 吃单|挂单 MaxSpace@OpMode==1 挂单失效距离 数字型(number) 0.5 SlidePrice 下单滑动价(元) 数字型(number) 0.1 MaxAmount 开仓最大单次下单量 数字型(number) 0.3 AddGoal 加仓间距(元) 数字型(number) 1 AddLine 加仓均价目标(元) 数字型(number) 0.8 ProfitGoal 平仓目标(元) 数字型(number) 0.5 Interval 轮询间隔(秒) 数字型(number) 1 RestoreIt 恢复进度 布尔型(true/false) false RestoreType@RestoreIt 持仓方向 下拉框(selected) 做多|做空 RestorePrice@RestoreIt 持仓均价 数字型(number) 0 RestoreAmount@RestoreIt 持仓数量 数字型(number) 0 RestoreProfit@RestoreIt 上次盈利 数字型(number) 0 SaveLocal 保存本地日志 布尔型(true/false) false StopLoss 止损(元) 数字型(number) 8 */ //全局变量 var TradeType = null;//交易类型 var OrgAccount = null;// var Counter = {s : 0, f: 0, m: 0};//计数器 s:成功次数 f:解套次数 m:止损次数 var LastProfit = 0;//最后盈利 var AllProfit = 0;//总盈利 var LastTicker = null;//最后行情 var maxHold = 0;//最大持有 function _N(v, precision) {//处理 数据, precision为要保留的小数位数,v是要处理的数据 if (typeof(precision) != 'number') {//没有传入precision 参数 或 传入的不是数字 默认 precision为4 precision = 4; } var d = parseFloat(v.toFixed(Math.max(10, precision+5)));//至少保留10位小数四舍五入,再转为浮点数。 s = d.toString().split(".");//把上面的浮点数d 按小数点分割 返回字符串数组 if (s.length < 2 || s[1].length <= precision) {//如果没有小数部分,或 小数部分位数长度小于等于参数precision,则 return d;//返回浮点数d } var b = Math.pow(10, precision);//计算 10的precision次幂。 return Math.floor(d*b)/b;//浮点数先乘b小数部分向下取整,再除b,实现了保留precision为小数,其余位数舍去 } function EnsureCall(e, method) {//调用确认 容错功能,现已有容错模板在策略广场 var r; while (!(r = e[method].apply(this, Array.prototype.slice.call(arguments).slice(2)))) { Sleep(Interval);//多少毫秒后重试 } return r; } function StripOrders(e, orderId) {//取消除了ID 为 orderId 外的其他未完成的订单,详见交易类库代码注释 var order = null; if (typeof(orderId) == 'undefined') { orderId = null; } while (true) { var dropped = 0; var orders = EnsureCall(e, 'GetOrders'); for (var i = 0; i < orders.length; i++) { if (orders[i].Id == orderId) { order = orders[i]; } else { var extra = ""; if (orders[i].DealAmount > 0) { extra = "成交: " + orders[i].DealAmount; } else { extra = "未成交"; } e.CancelOrder(orders[i].Id, orders[i].Type == ORDER_TYPE_BUY ? "买单" : "卖单", extra); dropped++; } } if (dropped == 0) { break; } Sleep(300); } return order; } function updateProfit(e, account, ticker) {//更新收益 if (typeof(account) == 'undefined') {//如果,没传入account,此刻获取。 account = GetAccount(e); } if (typeof(ticker) == 'undefined') {//如果,没传入行情,此刻获取。 ticker = EnsureCall(e, "GetTicker");//容错,确保获取。 } var profit = _N(LastProfit + (((account.Stocks + account.FrozenStocks) - (OrgAccount.Stocks + OrgAccount.FrozenStocks)) * ticker.Last) + ((account.Balance + account.FrozenBalance) - (OrgAccount.Balance + OrgAccount.FrozenBalance)), 4); //收益 = 上次收益 + (账户总币数 - org总币数)*此刻行情最后成交价 - (总钱 - org总钱) LogProfit(profit, "币数:", _N(account.Stocks + account.FrozenStocks, 4), "钱数:", _N(account.Balance + account.FrozenBalance, 4)); //记录且输出 return profit;//返回 } var preMsg = "";//之前的消息 function GetAccount(e, waitFrozen) {//获取账户信息, 第二个参数 为 是否等待 冻结 if (typeof(waitFrozen) == 'undefined') {//如果没有传入第二个参数 ,设置不等待冻结 waitFrozen = false; } var account = null; var alreadyAlert = false; while (true) { account = EnsureCall(e, "GetAccount"); if (!waitFrozen || (account.FrozenStocks < e.GetMinStock() && account.FrozenBalance < 0.01)) { break; } if (!alreadyAlert) { alreadyAlert = true; Log("发现账户有冻结的钱或币", account); } Sleep(Interval); } // TODO Hack msg = "成功: " + Counter.s + " 次, 解套: " + Counter.f + " 次, 止损: " + Counter.m + " 次, 最大持仓量: " + _N(maxHold); //msg = Counter.s + " / " + Counter.f + " / " + Counter.m; if (LastTicker != null && OrgAccount != null) {//如果最后的行情不等于空,且 最后的账户信息不等于空 var profit = (((account.Stocks + account.FrozenStocks) - (OrgAccount.Stocks + OrgAccount.FrozenStocks)) * LastTicker.Last) + ((account.Balance + account.FrozenBalance) - (OrgAccount.Balance + OrgAccount.FrozenBalance)); //计算收益。 //输出 总收益 本次收益 之前账户信息 当前账户信息 历史信息 msg += "\n盈亏: " + AllProfit + ", 浮动: " + _N(profit, 4); msg += "\n初始账户 钱: " + OrgAccount.Balance + " 币: " + OrgAccount.Stocks + ", 当前账户 钱: " + _N(account.Balance + account.FrozenBalance) + " 币: " + _N(account.Stocks + account.FrozenStocks); } if (msg != preMsg) {//本次msg信息已更新(与上次不同) preMsg = msg;//更新上次信息 LogStatus(msg, "#ff0000");//更新机器人状态 } return account;//返回账户信息 } // mode = 0 : direct buy, 1 : buy as buy1 交易函数 在交易类库模板有注释 function Trade(e, tradeType, tradeAmount, mode, slidePrice, maxAmount, maxSpace, retryDelay) { var initAccount = GetAccount(e, true); var nowAccount = initAccount; var orderId = null; var prePrice = 0; var dealAmount = 0; var diffMoney = 0; var isFirst = true; var tradeFunc = tradeType == ORDER_TYPE_BUY ? e.Buy : e.Sell; var isBuy = tradeType == ORDER_TYPE_BUY; while (true) { var ticker = EnsureCall(e, 'GetTicker'); LastTicker = ticker; var tradePrice = 0; if (isBuy) { tradePrice = _N((mode == 0 ? ticker.Sell : ticker.Buy) + slidePrice, 4); } else { tradePrice = _N((mode == 0 ? ticker.Buy : ticker.Sell) - slidePrice, 4); } if (orderId == null) { if (isFirst) { isFirst = false; } else { nowAccount = GetAccount(e, true); } var doAmount = 0; if (isBuy) { diffMoney = _N(initAccount.Balance - nowAccount.Balance, 4); dealAmount = _N(nowAccount.Stocks - initAccount.Stocks, 4); doAmount = Math.min(maxAmount, tradeAmount - dealAmount, _N((nowAccount.Balance-10) / tradePrice, 4)); } else { diffMoney = _N(nowAccount.Balance - initAccount.Balance, 4); dealAmount = _N(initAccount.Stocks - nowAccount.Stocks, 4); doAmount = Math.min(maxAmount, tradeAmount - dealAmount, nowAccount.Stocks); } if (doAmount < e.GetMinStock()) { break; } prePrice = tradePrice; orderId = tradeFunc(tradePrice, doAmount); } else { if (Math.abs(tradePrice - prePrice) > maxSpace) { orderId = null; } var order = StripOrders(exchange, orderId); if (order == null) { orderId = null; } } Sleep(retryDelay); } if (dealAmount <= 0) { return null; } return {price: _N(diffMoney / dealAmount, 4), amount: dealAmount}; } function loop(isFirst) {//循环 函数 核心功能模块 var minStock = exchange.GetMinStock();//获取交易所的最小交易币数 var initAccount = GetAccount(exchange, true);//获取当前账户信息 ,等待 冻结 Log(initAccount);//输出当前账户信息 var holdPrice = 0;//初始持仓价格为0 var holdAmount = 0;//初始持仓量为0 if (RestoreIt && isFirst) {//如果参数选择恢复进度为true,且程序是新启动状态(isFirst在main函数的while循环第一次循环为true,之后更新为false) LastProfit = RestoreProfit;//恢复最后收益 TradeType = RestoreType == 0 ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;//根据恢复的持仓方向(0:多,1:空) //确定交易类型 holdPrice = RestorePrice;//恢复持仓均价 holdAmount = RestoreAmount;//恢复持仓量 if (holdAmount != 0) {//如果恢复的持仓量不等于0,执行以下 initAccount = {//初始账户对象各属性 赋值 Stocks: initAccount.Stocks, FrozenStocks: initAccount.FrozenStocks, Balance: initAccount.Balance, FrozenBalance: initAccount.FrozenBalance, }; if (RestoreType == 0) {//如果恢复的持仓方向 为 多 执行以下 initAccount.Stocks -= holdAmount;//初始账户对象的币数中 减去 持仓量 initAccount.Balance += (holdPrice * holdAmount);//初始账户对象的钱数 加上 持仓量*持仓均价 //此条件内 作用是 把 账户状态恢复成 持仓前的状态。 } else {//恢复的持仓方向 为 空 执行以下 现货做空 initAccount.Stocks += holdAmount;// 初始账户 币数 加上 持仓量 initAccount.Balance -= (holdPrice * holdAmount);// 初始账户钱数 减去 持仓量 * 持仓均价 //同样为恢复到做空前的账户状态 } OrgAccount = initAccount;//此刻的initAccount赋给全局变量OrgAccount 。 //作用: Log("恢复持仓状态为:", RestoreType == 0 ? "做多" : "做空", "均价:", holdPrice, "数量:", holdAmount); //输出 恢复持仓的状态信息。。。 if (RestoreType == 0) {//如果 持仓方向为 多 holdAmount = Math.min(initAccount.Stocks, holdAmount);//限制准备的持仓量 不大于 持仓前账户的币数 } } if (LastProfit != 0) {//如果恢复的最后收益不等于0 执行以下 LogProfit(LastProfit, "恢复上次盈利");//输出 } }//恢复进度完成 if (holdAmount == 0) {//如果 持仓量 为 0 (恢复进度了持仓量为0,或者 没有恢复进度,持仓量为0),初次开仓操作 var obj = Trade(exchange, TradeType, OpAmount, OpMode, SlidePrice, MaxAmount, MaxSpace, Interval);//交易函数 //TradeType在main里已赋值, 开仓量,开仓方式,滑价,开仓最大单次下单量,挂单失效距离,轮询间隔 if (!obj) {//如果obj为null throw "出师不利, 开仓失败";//开仓失败 } else {//obj不为null Log(TradeType == ORDER_TYPE_BUY ? "开多仓完成" : "开空仓完成", "均价:", obj.price, "数量:", obj.amount); //输出 开仓完成的信息,开仓方向、均价、数量 } Log(GetAccount(exchange, true));//输出此刻账户信息 holdPrice = obj.price;//设置持仓均价 holdAmount = obj.amount;//设置持仓量 } var openFunc = TradeType == ORDER_TYPE_BUY ? exchange.Buy : exchange.Sell; //根据交易类型(多、空方向) 决定 openFunc(开仓函数) 交易类型 多为买开仓 空为卖开仓 var coverFunc = TradeType == ORDER_TYPE_BUY ? exchange.Sell : exchange.Buy; //根据交易类型 决定 coverFunc (平仓函数) 交易类型 多为卖平仓 空为买平仓 var isFinished = false;//设置一个 循环完成检测量,初始值为false while (!isFinished) {//循环 var account = GetAccount(exchange, true);//获取此刻账户信息 account var openAmount = 0;//初始开仓量0 () var openPrice = 0;//开仓价格0 var coverPrice = 0;//平仓价格0 var canOpen = true;//能否开仓初始true if (TradeType == ORDER_TYPE_BUY) {//如果开仓方向为 多 var upLine = AddLine;//加仓均价目标 赋给upLine openPrice = _N(holdPrice - AddGoal, 4);//持仓均价 - 加仓间距,赋给开仓价格,计算加仓价格 openAmount = _N((holdAmount * (holdPrice - openPrice - upLine)) / upLine, 4); //持仓量*(持仓均价-开仓价格-加仓均价目标)/加仓均价目标, 计算出来的是 加仓量 此处有推导公式过程 coverPrice = _N(holdPrice + ProfitGoal, 4);//持仓均价 + 平仓目标, 计算 平仓价 if (_N(account.Balance / openPrice, 4) < openAmount) {//如果账户钱按加仓价格 不足以 按加仓量 加仓 Log("没有钱加多仓, 需要加仓: ", openAmount, "个");//显示需要加仓的数量 canOpen = false;//设置 能否开仓 为false } } else {//开仓方向为 空 var upLine = -AddLine;//加仓均价目标 赋给upLine,因为做空,所以取反。 openPrice = _N(holdPrice + AddGoal, 4);// 计算加仓价格 ,因为做空 所以 是 加 coverPrice = _N(holdPrice - ProfitGoal, 4);//持仓均价 - 平仓目标,计算平仓价 openAmount = _N((holdAmount * (holdPrice - openPrice - upLine) / upLine), 4); //计算加仓量 if (account.Stocks < openAmount) {//如果账户 的币数 小于 加仓量 Log("没有币加空仓, 需要币:", openAmount);//输出信息 canOpen = false;//设置 能否开仓 为 false } } if (holdAmount < minStock) {//如果 持仓过小 执行以下 Log("剩余币数过小, 放弃操作", holdAmount);//输出信息 return 0;//函数返回,到main函数从新循环 } openAmount = Math.max(minStock, openAmount);//确保加仓量不小于交易所允许的最小值 var order_count = 0;//订单数 var openId = null;//开仓订单ID var coverId = null;//平仓订单ID if (!canOpen) {//如果不能加仓 openId = -1;//开仓订单ID赋值 -1 Log("进入等待解套模式");//没钱 加仓 } for (var i = 0; i < 10; i++) {//循环10次 if (!openId) {//如果openId为空 openId = openFunc(openPrice, openAmount);//开仓操作 } if (!coverId) {//如果coverId为空 coverId = coverFunc(coverPrice, holdAmount);//平仓操作 } if (openId && coverId) {//开仓 平仓 都执行了 才跳出 break; } Sleep(Interval);//轮询间隔 } if (!openId || !coverId) {//如果10次循环结束后 仍有开仓操作 或 平仓操作 没执行成功返回null StripOrders(exchange);//取消所有订单 throw "下单失败";//抛出错误 } if (openId > 0) {//如果不为解套模式 order_count++;//订单数累计1 } if (coverId > 0) {//如果平仓操作成功 order_count++;//订单数累计1 } var preAccount = account;//保存一下开仓平仓之前的账户信息 var loss = null;//损失 while (true) { Sleep(Interval); var ticker = EnsureCall(exchange, "GetTicker");//获取此刻行情 LastTicker = ticker;//保存此刻行情 var floatProfit = Math.abs(ticker.Last - coverPrice) * holdAmount;//此刻行情的最后成交价与平仓目标价格只差的绝对值*持仓量 = 浮动盈亏 var balance = false;//不止损 if (loss === null) {//此处的while循环第一次执行 loss = floatProfit;//记录第一循环时的盈亏 } else if (floatProfit - loss > StopLoss) {//每次循环到此判断 盈亏 增幅是否 大于止损值 Log("当前浮动盈亏:", floatProfit, "开始止损");//输出信息 StripOrders(exchange);//取消所有未完成的订单 balance = true;//止损 } var orders = EnsureCall(exchange, "GetOrders");//获得所有未完成的订单 var nowAccount = GetAccount(exchange);//获取此刻账户信息,不等待冻结 var diff = nowAccount.Stocks + nowAccount.FrozenStocks - preAccount.Stocks;//此刻账户总币数 - 加仓前账户币数 if (balance) {//止损 diff = nowAccount.Stocks + nowAccount.FrozenStocks - OrgAccount.Stocks;//修改过 此刻账户总币数 - 开仓前账户币数 if (Math.abs(diff) > minStock) {//币数差的绝对值 大于交易所允许的最小交易量 var obj = Trade(exchange, diff > 0 ? ORDER_TYPE_SELL : ORDER_TYPE_BUY, Math.abs(diff), 0, SlidePrice, MaxAmount, MaxSpace, Interval); //平仓 止损操作 if (!obj) {//如果 obj为空 throw "止损失败";//抛出错误 } else {//成功输出 信息 Log(TradeType == ORDER_TYPE_BUY ? "平空仓完成" : "平多仓完成", "均价:", obj.price, "数量:", obj.amount); //输出止损 平仓信息 平多仓 平空仓 } } nowAccount = GetAccount(exchange);//更新此刻账户信息 AllProfit = updateProfit(exchange, GetAccount(exchange), ticker);//更新总盈亏 initAccount = nowAccount;//把此刻账户信息 更新给 initAccount 下轮使用 isFinished = true;//外层 while循环 结束标记 为 true Counter.m++;//止损次数累计 break;//跳出当前循环 } if (orders.length != order_count || Math.abs(diff) >= minStock) { //如果 当前未完成的订单数不等于 订单计数(就是有完成的订单) 或 diff的绝对值大于等于 最小交易数 StripOrders(exchange);//取消所有未完成的订单 nowAccount = GetAccount(exchange, true);//获取此刻账户信息 //Log(nowAccount);//测试用 var diffAmount = nowAccount.Stocks - initAccount.Stocks;//计算币差量 var diffMoney = nowAccount.Balance - initAccount.Balance;//计算钱差量 if (Math.abs(diffAmount) < minStock) { AllProfit = updateProfit(exchange, nowAccount, ticker);//更新盈利 Log("平仓完成, 达到目标盈利点, 单次盈利", _N(holdAmount * ProfitGoal, 4)); initAccount = nowAccount;//更新initAccount isFinished = true;//外层while循环 结束标记 为true if (!canOpen) {//如果不能加仓 Counter.f++;//解套计数累计1 } break;//跳出当前while } var newHoldPrice = 0;//新持仓价 var newHoldAmount = 0;//新持仓量 if (TradeType == ORDER_TYPE_BUY) {//如果为 多 方向 newHoldAmount = _N(diffAmount, 4); newHoldPrice = _N((-diffMoney) / diffAmount, 4); } else {//空 方向 newHoldAmount = _N(-diffAmount, 4); newHoldPrice = _N(diffMoney / (-diffAmount), 4); } // if open again, we need adjust hold positions's price var isAdd = false;// if (newHoldAmount > holdAmount) {//如果新持仓量 大于 持仓量 holdPrice = newHoldPrice;//更新 持仓均价 isAdd = true;// 设置可以加仓 } holdAmount = newHoldAmount;//更新持仓量 maxHold = Math.max(holdAmount, maxHold);//设置最大持有量 最小为maxHold if (!isAdd) {//如果不能加仓 持仓前账户调整 // reset initAccount 重设 initAccount initAccount = { Stocks : nowAccount.Stocks, Balance : nowAccount.Balance, FrozenBalance : nowAccount.FrozenBalance, FrozenStocks : nowAccount.FrozenStocks, }; if (TradeType == ORDER_TYPE_BUY) {//多仓方向 initAccount.Stocks -= holdAmount; initAccount.Balance += holdAmount * holdPrice; } else {//空仓方向 initAccount.Stocks += holdAmount; initAccount.Balance -= holdAmount * holdPrice; } initAccount.Stocks = _N(initAccount.Stocks, 4);//处理数值 initAccount.Balance = _N(initAccount.Balance, 4);//处理数值 Log("持仓前账户调整为: ", initAccount); } Log((TradeType == ORDER_TYPE_BUY ? "多仓" : "空仓"), (isAdd ? "加仓后" : "平仓后"), "重新调整持仓, 均价: ", holdPrice, "数量", holdAmount); Log("买一:", ticker.Buy, "卖一:", ticker.Sell, "上次成交价:", ticker.Last); Log(nowAccount); break; } } } return 0; } function onexit() {//取消所有未完成的订单,扫尾 StripOrders(exchange); Log("Exit");//输出退出 } function main() { if (AddLine > AddGoal || AddLine <= 0) {//加仓均价目标(元)>加仓间距 或者 加仓均价目标(元)<= 0 throw "加仓均价目标错误"; } if (exchange.GetName().indexOf("Future") != -1) {//检索交易所 名称 出现 "Future"期货 抛出以下信息 throw "只支持现货, 期货容易爆仓, 暂不支持"; } if (exchange.GetRate() != 1) {//如果汇率不为1 Log("已禁用汇率转换"); exchange.SetRate(1);//禁用汇率转换 } TradeType = OpType == 0 ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;//根据开仓方向,确定做多?做空? EnableLogLocal(SaveLocal);//设置是否 储存在本地LOG ,API中 没写~ Interval *= 1000;//换算为毫秒 SetErrorFilter("502:|503:|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF"); //过滤错误信息 StripOrders(exchange);//取消所有未完成的 订单 OrgAccount = GetAccount(exchange);//获取当前账户信息 var isFirst = true;//初始isFirst 为true LogStatus("启动成功", TradeType); while (true) { var ret = loop(isFirst);//第一次执行特殊操作 isFirst = false;//更新isFirst为 false Counter.s++;//累计成功次数 Sleep(Interval);//间隔暂停 } }
Related Recommendations
Comment
All comments (7)

    破冰者和漏斗持仓法有何不同呢

    9 years ago

    其实 就是 逆势加仓 抗到趋势 有利, 除非资金量 无限,否则总有 一个 黑天鹅 在等待飞起。。。

    9 years ago

    你好聪明,

    10 years ago
    N

    img

    10 years ago

    简单的分析~先放出,研究透彻再发个全面点的。

    10 years ago

    梦神可以讲一下这个策略的主要逻辑吗 自己看还是有点摸不着头脑

    9 years ago

    可以 ,我简单说下:
    1、这个策略是 一个高频策略, 核心逻辑是 逆势加仓, 顺势盈利,所以 你看回测的时候 除了 发生止损,收益是一直向上的。
    2、顺势就不说了,很好理解,就是挂的 盈利平仓单成交,本轮循环结束。
    3、逆势就进行加仓,有个算法 具体根据 界面上的参数设置,计算出来 符合界面参数上 加仓间距 目标均价 等等设置的 加仓单价格和需要交易的数量。这样 持仓均价就会向有利的方向移动,直到行情反弹,持有的仓位平仓 盈利,否则就根据参数 一路 加仓 ,通过加仓去移动持仓均价。
    4、致命伤: 资金量, 如果资金量被加仓 操作 消耗殆尽,就是 被深套了,如果是期货可能就爆仓了。
    主要逻辑就是以上几点,但是 策略 细节很多。从一个简单想法到一个完备的策略程序,中间有很多细节。这些细节也决定最终效果!

    9 years ago
  • 1
Forums
PINE Language
Get the app
iPhone Download
© 2015 - ∞ INVENTOR PTE LTD (SG)