update
This commit is contained in:
@@ -1,329 +0,0 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
FMEX[空头]趋势挖矿策略(随机网格排序)
|
||||
|
||||
> 策略作者
|
||||
|
||||
古力十段
|
||||
|
||||
> 策略描述
|
||||
|
||||
FMex排序挖矿空头版本代码使用说明。(注意api地址)(加强版-增加仓位管理-增加时间设定,调整单量方向等-购买联系微信:ying5737)
|
||||
(预计日跌幅1%以上,赚币赚矿,反之亏损明显)
|
||||
保证金市场风险巨大,你可能随时面临100%损失。或有不明bug100%损失,概不负责。
|
||||
|
||||
原理:盘口下单随机成交/
|
||||
先持仓SHORT100u。//注意:先手动持仓SHORT100u。
|
||||
|
||||
- 检测现有订单是否超过界限,如果超过了就立马取消该订单/
|
||||
- 检测是否成交形成了仓位,如果大于仓位xxu,则减仓到既定仓位以下/
|
||||
|
||||
几种情况:
|
||||
全局挂单:为区别市商策略,特定定义远端排序为挂单,参数为买(35u)单量卖(35u)左右。参数可调。
|
||||
市商:
|
||||
最大多头持仓100u,若大于该仓位,约每6s减20u仓位,直到小于100u.
|
||||
最大空头持仓1000u,若大于该仓位,约每6s减200u仓位,直到小于100u.
|
||||
大于1u多仓启动减多仓策略,挂单为short:卖3卖4卖5(各30u单量)
|
||||
大于800u空仓,启动减空仓策略,挂单为long:买2买3买4(各30u单量)
|
||||
正常持仓,SHORT 卖2卖3卖4
|
||||
|
||||
//参数中的备注描述,仅供参考,另可加挡位
|
||||
风险自负/参数可调,购买加强版联系微信:ying5737
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Url|https://api.fmex.com|交易所api地址|
|
||||
|g_minPercent|0.05|挂单:挖矿的下限,离最新价的百分比,如果是 1% 则填写 1|
|
||||
|g_maxPercent|0.05|挂单:挖矿的上限,离最新价的百分比,如果是 1% 则填写 1|
|
||||
|g_perAmount|35|挂单:每个订单量多少张,最多只能挂50个订单|
|
||||
|g_kongtouAmount|35|挂单:|
|
||||
|g_maxHoldingLong|100|挂单:多单最大持仓量|
|
||||
|g_maxHoldingShort|1000|挂单:空单最大持仓量|
|
||||
|sp_jiancangAmount|true|市商/排序:减仓多仓|
|
||||
|sp_jiacangAmount|true|市商/排序:增加多仓|
|
||||
|sp_kongtouAmount|30|市商/排序: 空头数|
|
||||
|sp_perAmount|30|市商/排序:单个订单大小|
|
||||
|mainIntervel|false|Intervel|
|
||||
|Intervel|false|Intervel0|
|
||||
|sp_baseAmountLong|true|多仓|
|
||||
|sp_baseAmountShort|800|空仓|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
exchange.IO("base", Url)//保证金市场风险巨大,你可能随时面临100%损失。或有不明bug100%损失,概不负责。
|
||||
//实盘地址:https://api.fmex.com 测试网https://api.testnet.fmex.com//注意:先持仓short100u。
|
||||
var ordersInfo = {//(加强版-增加仓位管理-增加时间设定-调整单量方向等-价格美丽/购买联系微信:ying5737)
|
||||
buyId: 0,
|
||||
buyPrice: 0,
|
||||
sellId: 0,
|
||||
sellPrice: 0,
|
||||
minPrice: 0,
|
||||
maxPrice: 0
|
||||
}
|
||||
var depthInfo = {//注意:先手动持仓short100u。
|
||||
asks: [],
|
||||
bids: []
|
||||
}
|
||||
function getTicker(symbol) {
|
||||
url = "/v2/market/ticker/" + symbol;
|
||||
data = exchange.IO("api", "GET", url);
|
||||
return data.data;
|
||||
}
|
||||
function getAccounts() {
|
||||
data = exchange.IO("api", "GET", "/v3/contracts/accounts")
|
||||
return data.data;
|
||||
}
|
||||
|
||||
function createOrderPrice(body) {
|
||||
parameter = "symbol=" + body.symbol + "&type=" + body.type + "&direction=" + body.direction + "&post_only=" + body.post_only + "&price=" + body.price + "&quantity=" + body.quantity;
|
||||
|
||||
resultData = exchange.IO("api", "POST", "/v3/contracts/orders", parameter)
|
||||
return resultData;
|
||||
}
|
||||
function createOrder(body) {
|
||||
parameter = "symbol=" + body.symbol + "&type=" + body.type + "&direction=" + body.direction + "&quantity=" + body.quantity;
|
||||
|
||||
resultData = exchange.IO("api", "POST", "/v3/contracts/orders", parameter)
|
||||
|
||||
return resultData;
|
||||
}
|
||||
|
||||
function getOrders() {
|
||||
resultData = exchange.IO("api", "GET", "/v3/contracts/orders/open");
|
||||
return resultData.data
|
||||
}
|
||||
|
||||
function cancelOrder(id) {
|
||||
resultData = exchange.IO("api", "POST", "/v3/contracts/orders/" + id + "/cancel");
|
||||
return resultData;
|
||||
|
||||
}
|
||||
|
||||
function getPosition() {
|
||||
resultData = exchange.IO("api", "GET", "/v3/broker/auth/contracts/positions");
|
||||
return resultData.data;
|
||||
}
|
||||
|
||||
function getMatches(id) {
|
||||
resultData = exchange.IO("api", "GET", "/v3/contracts/orders/" + id + "/matches");
|
||||
return resultData.data;
|
||||
}
|
||||
|
||||
function getCandles(resolution, symbol) {
|
||||
resultData = exchange.IO("api", "GET", "/v2/market/candles/" + resolution + "/" + symbol);
|
||||
return resultData.data;
|
||||
}
|
||||
function cleanOrders(lastPrice) {
|
||||
ordersInfo = getOrders();
|
||||
orders = ordersInfo.results;
|
||||
for (var i = 0; i < orders.length; i++) {
|
||||
diffPercent = Math.abs(lastPrice - orders[i].price) / lastPrice * 100
|
||||
if (diffPercent < g_minPercent || diffPercent > g_maxPercent) {
|
||||
orderInfo = cancelOrder(orders[i].id);
|
||||
|
||||
Log("挂单取消订单:");
|
||||
}
|
||||
}
|
||||
}
|
||||
function getToOrderPrice(basePrice, direct) {
|
||||
return parseInt(basePrice * (direct * (g_minPercent + Math.random() * (g_maxPercent - g_minPercent)) / 100 + 1))
|
||||
}
|
||||
function cleanPosition() {
|
||||
res = getPosition();
|
||||
res.results.forEach(function(it) {
|
||||
if (it.symbol == 'BTCUSD_P') {
|
||||
if (it.quantity) {
|
||||
if (it.quantity > g_maxHoldingLong && it.direction.toUpperCase() == 'LONG') {
|
||||
data = createOrder({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "MARKET",
|
||||
direction: it.direction.toUpperCase() == 'LONG' ? "SHORT" : "LONG",
|
||||
quantity: 20,
|
||||
})
|
||||
Log("平仓:");
|
||||
}
|
||||
if (it.quantity > g_maxHoldingShort && it.direction.toUpperCase() == 'SHORT') {
|
||||
data = createOrder({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "MARKET",
|
||||
direction: it.direction.toUpperCase() == 'LONG' ? "SHORT" : "LONG",
|
||||
quantity: 20,
|
||||
})
|
||||
Log("平仓:");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function guadan() {
|
||||
tickerInfo = getTicker('BTCUSD_P');
|
||||
ticker = tickerInfo.ticker;
|
||||
|
||||
var lastPrice = ticker[0];
|
||||
Log("lastPrice: "+lastPrice);
|
||||
buyPrice = getToOrderPrice(lastPrice, -1);
|
||||
sellPrice = getToOrderPrice(lastPrice, 1);
|
||||
|
||||
LongOrderInfo = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "LONG",
|
||||
post_only: true,
|
||||
price: buyPrice,
|
||||
quantity: g_perAmount,
|
||||
})
|
||||
Log("挂单LONG:" );
|
||||
shortOrderInfo = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: sellPrice,
|
||||
quantity: g_kongtouAmount,
|
||||
})
|
||||
Log("挂单SHORT:" );
|
||||
|
||||
cleanOrders(lastPrice)
|
||||
cleanPosition();
|
||||
}
|
||||
function shishang() {
|
||||
Log("市商/排序挖矿**********************************");
|
||||
tickerInfo = getTicker('BTCUSD_P');
|
||||
ticker = tickerInfo.ticker;
|
||||
Log("市商/排序挖矿ticker:");
|
||||
lastPrice = ticker[0]
|
||||
buyPrice = ticker[2]
|
||||
sellPrice = ticker[4]
|
||||
Log("lastPrice:"+lastPrice+":buyPrice:"+buyPrice+":sellPrice:" + sellPrice);
|
||||
if (lastPrice == buyPrice) {
|
||||
sellPrice = (buyPrice + 0.5).toFixed(1)
|
||||
}
|
||||
if (lastPrice == sellPrice) {
|
||||
buyPrice = (sellPrice - 0.5).toFixed(1)
|
||||
}
|
||||
Log("buyPrice:"+buyPrice+":sellPrice:" + sellPrice);
|
||||
// const LastK7 = k[7]; // 最近第九根
|
||||
res = getPosition();
|
||||
Log("市商/排序挖矿Position:" + JSON.stringify(res));
|
||||
res.results.forEach(function(it) {
|
||||
|
||||
if (it.symbol == 'BTCUSD_P') {
|
||||
if (it.quantity) {
|
||||
if (it.quantity > sp_baseAmountLong && it.direction.toUpperCase() == 'LONG') {
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice + 1.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("排序10:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice + 2,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("排序9:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice + 2.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("排序8:");
|
||||
} else if (it.quantity > sp_baseAmountShort && it.direction.toUpperCase() == 'SHORT') {
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "LONG",
|
||||
post_only: true,
|
||||
price: lastPrice - 0.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商/排序-2:");
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "LONG",
|
||||
post_only: true,
|
||||
price: lastPrice - 1,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商/排序-3:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "LONG",
|
||||
post_only: true,
|
||||
price: lastPrice - 1.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商/排序-4:" );
|
||||
} else {
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice - 1,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商-3:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice - 1.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商-4:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice - 2,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商-5:");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
/********************END 市商/排序挖矿***************************************************************************************************/
|
||||
function main() {
|
||||
|
||||
while(true){
|
||||
|
||||
guadan();
|
||||
Sleep(Intervel * 1000)
|
||||
|
||||
shishang()
|
||||
Sleep(mainIntervel * 1000)
|
||||
}
|
||||
}//FMex排序挖矿多头版本代码使用说明。(注意api地址)(加强版-增加仓位管理-增加时间设定等-购买联系微信:ying5737)
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/178417
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-12-21 07:34:12
|
||||
@@ -1,359 +0,0 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
FMEX挖矿策略(排序)btc均衡或【震荡版】
|
||||
|
||||
> 策略作者
|
||||
|
||||
古力十段
|
||||
|
||||
> 策略描述
|
||||
|
||||
FMex排序挖矿均衡代码使用说明。(注意api地址)(加强版-增加仓位管理-增加时间设定,调整单量,方向等-购买联系微信:ying5737)
|
||||
|
||||
保证金市场风险巨大,你可能随时面临100%损失。或有不明bug100%损失,概不负责。
|
||||
|
||||
原理:盘口下单随机成交/
|
||||
//注意:先手动持仓long1u。
|
||||
|
||||
- 检测现有订单是否超过界限,如果超过了就立马取消该订单/
|
||||
- 检测是否成交形成了仓位,如果大于仓位xxu,则减仓到既定仓位以下/
|
||||
|
||||
几种情况:
|
||||
全局挂单:为区别市商策略,特定定义远端排序为挂单,参数为买(35u)单量卖(35u)左右。参数可调。
|
||||
市商:
|
||||
最大多头持仓700u,若大于该仓位,约每6s减20u仓位,直到小于700u.
|
||||
最大空头持仓700u,若大于该仓位,约每6s减20u仓位,直到小于700u.
|
||||
大于500u多仓启动减多仓策略,挂单为short:卖3卖4卖5(各30u单量)
|
||||
大于500u空仓,启动减空仓策略,挂单为long:买2买3买4(各30u单量)
|
||||
正常持仓,SHORT 卖7卖8卖9 long买4买7买8
|
||||
|
||||
|
||||
|
||||
//可其他账户持仓对冲,对行情有把握后,亦可以单独开
|
||||
|
||||
//参数中的备注描述,仅供参考,另可加挡位
|
||||
风险自负/参数可调,购买加强版联系微信:ying5737
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Url|https://api.fmex.com|交易所api地址|
|
||||
|g_minPercent|0.05|挂单:挖矿的下限,离最新价的百分比,如果是 1% 则填写 1|
|
||||
|g_maxPercent|0.05|挂单:挖矿的上限,离最新价的百分比,如果是 1% 则填写 1|
|
||||
|g_perAmount|35|挂单:每个订单量多少张,最多只能挂50个订单|
|
||||
|g_kongtouAmount|35|挂单:|
|
||||
|g_maxHoldingLong|700|挂单:多单最大持仓量|
|
||||
|g_maxHoldingShort|700|挂单:空单最大持仓量|
|
||||
|sp_jiancangAmount|true|市商/排序:减仓多仓|
|
||||
|sp_jiacangAmount|true|市商/排序:增加多仓|
|
||||
|sp_kongtouAmount|30|市商/排序: 空头数|
|
||||
|sp_perAmount|30|市商/排序:单个订单大小|
|
||||
|mainIntervel|false|Intervel|
|
||||
|Intervel|false|Intervel0|
|
||||
|sp_baseAmountLong|500|多仓|
|
||||
|sp_baseAmountShort|500|空仓|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
exchange.IO("base", Url)//保证金市场风险巨大,你可能随时面临100%损失。或有不明bug100%损失,概不负责。
|
||||
//实盘地址:https://api.fmex.com 测试网https://api.testnet.fmex.com//注意:先持仓long1u。
|
||||
var ordersInfo = {//(加强版-增加仓位管理-增加时间设定-调整单量方向等-价格美丽/购买联系微信:ying5737)
|
||||
buyId: 0,
|
||||
buyPrice: 0,
|
||||
sellId: 0,
|
||||
sellPrice: 0,
|
||||
minPrice: 0,
|
||||
maxPrice: 0
|
||||
}
|
||||
var depthInfo = {//注意:先手动持仓long1u。
|
||||
asks: [],
|
||||
bids: []
|
||||
}
|
||||
function getTicker(symbol) {
|
||||
url = "/v2/market/ticker/" + symbol;
|
||||
data = exchange.IO("api", "GET", url);
|
||||
return data.data;
|
||||
}
|
||||
function getAccounts() {
|
||||
data = exchange.IO("api", "GET", "/v3/contracts/accounts")
|
||||
return data.data;
|
||||
}
|
||||
|
||||
function createOrderPrice(body) {
|
||||
parameter = "symbol=" + body.symbol + "&type=" + body.type + "&direction=" + body.direction + "&post_only=" + body.post_only + "&price=" + body.price + "&quantity=" + body.quantity;
|
||||
|
||||
resultData = exchange.IO("api", "POST", "/v3/contracts/orders", parameter)
|
||||
return resultData;
|
||||
}
|
||||
function createOrder(body) {
|
||||
parameter = "symbol=" + body.symbol + "&type=" + body.type + "&direction=" + body.direction + "&quantity=" + body.quantity;
|
||||
|
||||
resultData = exchange.IO("api", "POST", "/v3/contracts/orders", parameter)
|
||||
|
||||
return resultData;
|
||||
}
|
||||
|
||||
function getOrders() {
|
||||
resultData = exchange.IO("api", "GET", "/v3/contracts/orders/open");
|
||||
return resultData.data
|
||||
}
|
||||
|
||||
function cancelOrder(id) {
|
||||
resultData = exchange.IO("api", "POST", "/v3/contracts/orders/" + id + "/cancel");
|
||||
return resultData;
|
||||
|
||||
}
|
||||
|
||||
function getPosition() {
|
||||
resultData = exchange.IO("api", "GET", "/v3/broker/auth/contracts/positions");
|
||||
return resultData.data;
|
||||
}
|
||||
|
||||
function getMatches(id) {
|
||||
resultData = exchange.IO("api", "GET", "/v3/contracts/orders/" + id + "/matches");
|
||||
return resultData.data;
|
||||
}
|
||||
|
||||
function getCandles(resolution, symbol) {
|
||||
resultData = exchange.IO("api", "GET", "/v2/market/candles/" + resolution + "/" + symbol);
|
||||
return resultData.data;
|
||||
}
|
||||
function cleanOrders(lastPrice) {
|
||||
ordersInfo = getOrders();
|
||||
orders = ordersInfo.results;
|
||||
for (var i = 0; i < orders.length; i++) {
|
||||
diffPercent = Math.abs(lastPrice - orders[i].price) / lastPrice * 100
|
||||
if (diffPercent < g_minPercent || diffPercent > g_maxPercent) {
|
||||
orderInfo = cancelOrder(orders[i].id);
|
||||
|
||||
Log("挂单取消订单:");
|
||||
}
|
||||
}
|
||||
}
|
||||
function getToOrderPrice(basePrice, direct) {
|
||||
return parseInt(basePrice * (direct * (g_minPercent + Math.random() * (g_maxPercent - g_minPercent)) / 100 + 1))
|
||||
}
|
||||
function cleanPosition() {
|
||||
res = getPosition();
|
||||
res.results.forEach(function(it) {
|
||||
if (it.symbol == 'BTCUSD_P') {
|
||||
if (it.quantity) {
|
||||
if (it.quantity > g_maxHoldingLong && it.direction.toUpperCase() == 'LONG') {
|
||||
data = createOrder({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "MARKET",
|
||||
direction: it.direction.toUpperCase() == 'LONG' ? "SHORT" : "LONG",
|
||||
quantity: 20,
|
||||
})
|
||||
Log("平仓:");
|
||||
}
|
||||
if (it.quantity > g_maxHoldingShort && it.direction.toUpperCase() == 'SHORT') {
|
||||
data = createOrder({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "MARKET",
|
||||
direction: it.direction.toUpperCase() == 'LONG' ? "SHORT" : "LONG",
|
||||
quantity: 20,
|
||||
})
|
||||
Log("平仓:");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function guadan() {
|
||||
tickerInfo = getTicker('BTCUSD_P');
|
||||
ticker = tickerInfo.ticker;
|
||||
|
||||
var lastPrice = ticker[0];
|
||||
Log("lastPrice: "+lastPrice);
|
||||
buyPrice = getToOrderPrice(lastPrice, -1);
|
||||
sellPrice = getToOrderPrice(lastPrice, 1);
|
||||
|
||||
LongOrderInfo = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "LONG",
|
||||
post_only: true,
|
||||
price: buyPrice,
|
||||
quantity: g_perAmount,
|
||||
})
|
||||
Log("挂单LONG:" );
|
||||
shortOrderInfo = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: sellPrice,
|
||||
quantity: g_kongtouAmount,
|
||||
})
|
||||
Log("挂单SHORT:" );
|
||||
|
||||
cleanOrders(lastPrice)
|
||||
cleanPosition();
|
||||
}
|
||||
function shishang() {
|
||||
Log("市商/排序挖矿**********************************");
|
||||
tickerInfo = getTicker('BTCUSD_P');
|
||||
ticker = tickerInfo.ticker;
|
||||
Log("市商/排序挖矿ticker:");
|
||||
lastPrice = ticker[0]
|
||||
buyPrice = ticker[2]
|
||||
sellPrice = ticker[4]
|
||||
Log("lastPrice:"+lastPrice+":buyPrice:"+buyPrice+":sellPrice:" + sellPrice);
|
||||
if (lastPrice == buyPrice) {
|
||||
sellPrice = (buyPrice + 0.5).toFixed(1)
|
||||
}
|
||||
if (lastPrice == sellPrice) {
|
||||
buyPrice = (sellPrice - 0.5).toFixed(1)
|
||||
}
|
||||
Log("buyPrice:"+buyPrice+":sellPrice:" + sellPrice);
|
||||
// const LastK7 = k[7];
|
||||
res = getPosition();
|
||||
Log("市商/排序挖矿Position:" + JSON.stringify(res));
|
||||
res.results.forEach(function(it) {
|
||||
|
||||
if (it.symbol == 'BTCUSD_P') {
|
||||
if (it.quantity) {
|
||||
if (it.quantity > sp_baseAmountLong && it.direction.toUpperCase() == 'LONG') {
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice + 1.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("排序10:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice + 2,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("排序9:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice + 2.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("排序8:");
|
||||
} else if (it.quantity > sp_baseAmountShort && it.direction.toUpperCase() == 'SHORT') {
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "LONG",
|
||||
post_only: true,
|
||||
price: lastPrice - 0.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商/排序-2:");
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "LONG",
|
||||
post_only: true,
|
||||
price: lastPrice - 1,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商/排序-3:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "LONG",
|
||||
post_only: true,
|
||||
price: lastPrice - 1.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商/排序-4:" );
|
||||
} else {
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice - 3.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商-3:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice - 4.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商-4:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "SHORT",
|
||||
post_only: true,
|
||||
price: lastPrice - 4,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商-5:");
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "LONG",
|
||||
post_only: true,
|
||||
price: lastPrice - 2,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商/排序-3:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "LONG",
|
||||
post_only: true,
|
||||
price: lastPrice - 3.5,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商/排序-4:" );
|
||||
order = createOrderPrice({
|
||||
symbol: "BTCUSD_P",
|
||||
type: "LIMIT",
|
||||
direction: "LONG",
|
||||
post_only: true,
|
||||
price: lastPrice - 4,
|
||||
quantity: sp_perAmount,
|
||||
})
|
||||
Log("市商/排序-4:" );
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
/********************END 市商/排序挖矿***************************************************************************************************/
|
||||
function main() {
|
||||
|
||||
while(true){
|
||||
|
||||
guadan();
|
||||
Sleep(Intervel * 1000)
|
||||
|
||||
shishang()
|
||||
Sleep(mainIntervel * 1000)
|
||||
}
|
||||
}//FMex排序挖矿多头版本代码使用说明。(注意api地址)(加强版-增加仓位管理-增加时间设定等-购买联系微信:ying5737)
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/178700
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-12-21 07:31:42
|
||||
@@ -1,169 +0,0 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
MACD交易策略btc合约版
|
||||
|
||||
> 策略作者
|
||||
|
||||
groot
|
||||
|
||||
> 策略描述
|
||||
|
||||
一个简单的macd策略
|
||||
周期四小时
|
||||
金叉做多
|
||||
死叉做空
|
||||
杠杆均为一倍
|
||||
因为是反向系统,不带止损
|
||||
回测了下两年数据,币本位年化130%,最大回撤30%
|
||||
|
||||
另外回测了另一种macd策略
|
||||
水上做多
|
||||
水下做空
|
||||
杠杆均一倍,也不带止损
|
||||
回测了两年数据,币本位才30%,回撤15%左右。
|
||||
|
||||
所以可以得出,金叉死叉的指标有效性要高很多;
|
||||
其实从逻辑上来讲也容易理解,金叉反应的是快线拐头向上,代表的是一种趋势方向,水上反应的只是当前时间点的差值,并不能很好反应方向;
|
||||
如果用快线的窗口差值当做一个指标,比如当前快线和上个快线的差值大于某个阀值就做多,那也能达到跟金叉同样的效果;只是如果阀值设置不合理,有可能在震荡行情中,会频繁产生无效交易信号。
|
||||
|
||||
另外,周期分别回测了小时线、四小时线、日线,指标有效性排序:日线>四小时线>小时线;其实也很好理解,金叉死叉属于趋势策略,周期越长有效性越高。
|
||||
|
||||
关于macd的第三个参数,移动平均周期,默认设置的9,改成10效果会更好,该值越大,快线慢线的间隔越大,也就越能避免震荡行情频繁产生无效交易信号,不过太大的话,进入趋势行情的时间就晚了。
|
||||
|
||||
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|short|12|short|
|
||||
|long|26|long|
|
||||
|m|10|m|
|
||||
|threshold|5|threshold|
|
||||
|fresh_rete|4|交易频率(小时)|
|
||||
|
||||
|
||||
> 源码 (python)
|
||||
|
||||
``` python
|
||||
# 回测配置
|
||||
'''backtest
|
||||
start: 2018-01-01 00:00:00
|
||||
end: 2020-03-01 00:00:00
|
||||
period: 1d
|
||||
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD","stocks":1}]
|
||||
'''
|
||||
|
||||
mp = 0 # 定义一个全局变量,用于控制虚拟持仓
|
||||
counts = 0 # 定义一个全局变量,用于控制开单张数
|
||||
|
||||
|
||||
# 判断两根两个数组是否金叉
|
||||
def is_up_cross(arr1, arr2):
|
||||
if arr1[-2] < arr2[-2] and arr1[-1] > arr2[-1]:
|
||||
return True
|
||||
|
||||
# 判断两根两个数组是否死叉
|
||||
def is_down_cross(arr1, arr2):
|
||||
if arr1[-2] > arr2[-2] and arr1[-1] < arr2[-1]:
|
||||
return True
|
||||
|
||||
# 判断水上
|
||||
def is_up(arr1):
|
||||
if arr1[-1] > threshold:
|
||||
return True
|
||||
|
||||
# 判断水下
|
||||
def is_down(arr1):
|
||||
if arr1[-1] < -threshold:
|
||||
return True
|
||||
|
||||
|
||||
# 程序主函数
|
||||
def onTick():
|
||||
exchange.SetContractType("quarter")
|
||||
#金本位合约
|
||||
#exchange.IO("currency", "BTC_USDT")
|
||||
bar_arr = exchange.GetRecords(fresh_rete*60*60) # 获取4小时的K线数组
|
||||
#if _D(bar_arr[-1]['Time']/1000) < '2020-01-05 00:00:00':
|
||||
# Log("bar_arr",len(bar_arr))
|
||||
if len(bar_arr) < long + m + 1: # 如果K线数组长度太小,就不能计算MACD,所以直接返回跳过
|
||||
return
|
||||
all_macd = TA.MACD(bar_arr, short, long, m) # 计算MACD值,返回的是一个二维数组
|
||||
dif = all_macd[0] # 获取DIF的值,返回一个数组
|
||||
dif.pop() # 删除DIF数组最后一个元素,因为最后一跟k线还未形成
|
||||
dea = all_macd[1] # 获取DEA的值,返回一个数组
|
||||
dea.pop() # 删除DEA数组最后一个元素,因为最后一跟k线还未形成
|
||||
last_close = bar_arr[-1]['Close'] # 获取最新价格(卖价),用于开平仓
|
||||
global mp # 全局变量,用于控制虚拟持仓
|
||||
global counts # 全局变量,用于记录上次开单张数
|
||||
|
||||
|
||||
|
||||
#杠杆倍数
|
||||
acct = exchange.GetAccount()
|
||||
portfolio_value = (acct.Stocks+acct.FrozenStocks)*last_close
|
||||
counts_tmp = int(portfolio_value/100)
|
||||
|
||||
#开仓
|
||||
if mp == 0 :
|
||||
#多单
|
||||
if is_up_cross(dif, dea):
|
||||
#if is_up(dif):
|
||||
exchange.SetDirection("buy") # 设置交易方向和类型
|
||||
exchange.Buy(last_close + 10, counts_tmp) # 开多单
|
||||
counts = counts_tmp
|
||||
Log("bar_arr",bar_arr[-1],"dif",dif[-1],"dea",dea[-1])
|
||||
mp = 1 # 设置虚拟持仓的值,即有多单
|
||||
|
||||
#空单
|
||||
if is_down_cross(dif, dea):
|
||||
#if is_down(dif):
|
||||
exchange.SetDirection("sell") # 设置交易方向和类型
|
||||
exchange.Sell(last_close - 10, counts_tmp) # 开空单
|
||||
counts = counts_tmp
|
||||
Log("bar_arr",bar_arr[-1],"dif",dif[-1],"dea",dea[-1])
|
||||
mp = -1 # 设置虚拟持仓的值,即有空单
|
||||
|
||||
#平多开空
|
||||
if mp == 1 and is_down_cross(dif, dea):
|
||||
#if mp == 1 and is_down(dif,):
|
||||
exchange.SetDirection("closebuy") # 设置交易方向和类型
|
||||
exchange.Sell(last_close - 10, counts) # 平多单
|
||||
|
||||
exchange.SetDirection("sell") # 设置交易方向和类型
|
||||
exchange.Sell(last_close - 10, counts_tmp) # 开空单
|
||||
counts = counts_tmp
|
||||
Log("bar_arr",bar_arr[-1],"dif",dif[-1],"dea",dea[-1])
|
||||
mp = -1
|
||||
|
||||
# 平空开多
|
||||
if mp == -1 and is_up_cross(dif, dea):
|
||||
#if mp == -1 and is_up(dif):
|
||||
exchange.SetDirection("closesell") # 设置交易方向和类型
|
||||
exchange.Buy(last_close + 10, counts) # 平空单
|
||||
|
||||
exchange.SetDirection("buy") # 设置交易方向和类型
|
||||
exchange.Buy(last_close + 10, counts_tmp) # 开多单
|
||||
counts = counts_tmp
|
||||
Log("bar_arr",bar_arr[-1],"dif",dif[-1],"dea",dea[-1])
|
||||
mp = 1
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
while True:
|
||||
onTick()
|
||||
Sleep(fresh_rete*60*60*1000)
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/188100
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-03-06 12:01:46
|
||||
@@ -0,0 +1,219 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
MACD低买高卖自动跟单滑动止损
|
||||
|
||||
> 策略作者
|
||||
|
||||
John
|
||||
|
||||
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|ac1|0.005|交叉后第一柱|
|
||||
|bc1|-1e-06|交叉后前一柱|
|
||||
|SlidePrice|0.3|滑动价格|
|
||||
|TrailingStop|0.5|止损点|
|
||||
|orderTimeout|30|买单超时(秒)|
|
||||
|MinStock|30|最小持仓|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
|
||||
|
||||
Fixed = function(v) {
|
||||
return Math.floor(v*1000)/1000;
|
||||
};
|
||||
|
||||
// for orders
|
||||
WaitOrder = function(exchange, orderId, timeoutToCancel) {
|
||||
var ts = (new Date()).getTime();
|
||||
while (true) {
|
||||
Sleep(3000);
|
||||
var orderInfo = exchange.GetOrder(orderId);
|
||||
if (!orderInfo) {
|
||||
continue;
|
||||
}
|
||||
if (orderInfo.Status == ORDER_STATE_CLOSED || orderInfo.Status == ORDER_STATE_CANCELED) {
|
||||
return orderInfo;
|
||||
}
|
||||
if (((new Date()).getTime() - ts) > timeoutToCancel) {
|
||||
exchange.CancelOrder(orderId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Buy = function(exchange, maxPrice, slidePrice, balanceRatio, timeoutS) {
|
||||
var ts = (new Date()).getTime();
|
||||
var account;
|
||||
var dealAmount = 0.0;
|
||||
var usedBlance = 0.0;
|
||||
var maxBalanceUse = 0.0;
|
||||
var isFirst = true;
|
||||
do {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
Sleep(3000);
|
||||
}
|
||||
|
||||
var ticker = exchange.GetTicker();
|
||||
if (!ticker) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var buyPrice = ticker.Sell + slidePrice;
|
||||
|
||||
// Price too high, wait...
|
||||
if (buyPrice > maxPrice) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Initialize at first
|
||||
if (!account) {
|
||||
account = exchange.GetAccount();
|
||||
if (!account) {
|
||||
continue;
|
||||
}
|
||||
// Initialize maxBalanceUse
|
||||
maxBalanceUse = account.Balance * balanceRatio;
|
||||
}
|
||||
|
||||
var buyAmount = Fixed((maxBalanceUse - usedBlance) / buyPrice);
|
||||
if (buyAmount < MinStock) {
|
||||
break;
|
||||
}
|
||||
|
||||
orderId = exchange.Buy(buyPrice, buyAmount);
|
||||
if (!orderId) {
|
||||
Log(buyPrice, buyAmount, maxBalanceUse, usedBlance);
|
||||
continue;
|
||||
}
|
||||
|
||||
var orderInfo = WaitOrder(exchange, orderId, timeoutS);
|
||||
dealAmount += orderInfo.DealAmount;
|
||||
usedBlance += orderInfo.Price * orderInfo.DealAmount;
|
||||
if (orderInfo.Status == ORDER_STATE_CLOSED) {
|
||||
break;
|
||||
}
|
||||
} while (((new Date()).getTime() - ts) < timeoutS);
|
||||
|
||||
return {amount: dealAmount, price: (dealAmount > 0 ? usedBlance / dealAmount : 0)};
|
||||
};
|
||||
|
||||
Sell = function(exchange, sellAmount, slidePrice) {
|
||||
// Account info must set
|
||||
var account = exchange.GetAccount();
|
||||
while (!account) {
|
||||
Sleep(2000);
|
||||
account = exchange.GetAccount();
|
||||
}
|
||||
|
||||
sellAmount = Math.min(sellAmount, account.Stocks);
|
||||
|
||||
var cash = 0.0;
|
||||
var remain = sellAmount;
|
||||
|
||||
while (remain >= exchange.GetMinStock()) {
|
||||
var ticker = exchange.GetTicker();
|
||||
if (!ticker) {
|
||||
Sleep(2000);
|
||||
continue;
|
||||
}
|
||||
var sellPrice = ticker.Buy - slidePrice;
|
||||
var sellOrderId = exchange.Sell(sellPrice, remain);
|
||||
if (!sellOrderId) {
|
||||
Sleep(2000);
|
||||
continue;
|
||||
}
|
||||
var orderInfo = WaitOrder(exchange, sellOrderId, 10000);
|
||||
remain -= orderInfo.DealAmount;
|
||||
cash += orderInfo.Price * orderInfo.DealAmount;
|
||||
}
|
||||
return {amount: sellAmount, price: (sellAmount > 0 ? cash / sellAmount : 0)};
|
||||
};
|
||||
|
||||
var BuyInfo;
|
||||
var BanlanceRatio = 1.0;
|
||||
var Profit = 0.0;
|
||||
var timeAtBuy = 0;
|
||||
|
||||
function onTick(exchange) {
|
||||
var ticker = exchange.GetTicker();
|
||||
var records = exchange.GetRecords();
|
||||
if (!ticker || !records || records.length < 45) {
|
||||
return;
|
||||
}
|
||||
|
||||
var ticks = [];
|
||||
for (var i = 0; i < records.length; i++) {
|
||||
ticks.push(records[i].Close);
|
||||
}
|
||||
|
||||
var macd = TA.MACD(records, 12, 26, 9);
|
||||
var dif = macd[0];
|
||||
var dea = macd[1];
|
||||
var his = macd[2];
|
||||
|
||||
var op = 0;
|
||||
if (!BuyInfo) {
|
||||
if (dif[ticks.length-1] > 0 && his[ticks.length-1] > ac1 && his[ticks.length-2] < bc1) {
|
||||
op = 1;
|
||||
}
|
||||
} else {
|
||||
if (records[records.length-2].Time > timeAtBuy && records[records.length-1].Close < records[records.length-1].Open - 0.5
|
||||
&& records[records.length-2].Close < records[records.length-2].Open - 0.5
|
||||
&& records[records.length-1].Close < records[records.length-2].Close - 0.5) {
|
||||
op = 2;
|
||||
} else if (records[records.length-2].Time > timeAtBuy && BuyInfo.price > records[records.length-1].Close && records[records.length-1].Close < records[records.length-1].Open - 0.5) {
|
||||
op = 2;
|
||||
} else if ((BuyInfo.price < ticker.Last || dif[ticks.length-1] < 0) && his[ticks.length-1] <= 0) {
|
||||
op = 2;
|
||||
} else if ((BuyInfo.price > ticker.Last) && ((BuyInfo.price - ticker.Last) / BuyInfo.price > TrailingStop)) {
|
||||
op = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (op == 1) {
|
||||
var info = Buy(exchange, ticker.Sell + (SlidePrice * 3), SlidePrice, BanlanceRatio, orderTimeout * 1000);
|
||||
if (info.amount > 0) {
|
||||
BuyInfo = info;
|
||||
timeAtBuy = records[records.length-1].Time;
|
||||
}
|
||||
} else if (op == 2) {
|
||||
var info = Sell(exchange, BuyInfo.amount, SlidePrice);
|
||||
if (info.amount > 0) {
|
||||
Profit += info.amount * (info.price - BuyInfo.price);
|
||||
LogProfit(Profit);
|
||||
BuyInfo = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
var account = exchange.GetAccount();
|
||||
if (account) {
|
||||
Log(exchange.GetName(), exchange.GetCurrency(), account);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
onTick(exchange);
|
||||
Sleep(30000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/194224
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-14 12:00:26
|
||||
@@ -4,11 +4,28 @@
|
||||
> 开源策略
|
||||
|
||||
|
||||
- [商品期货多品种主力自动移仓海龟交易策略](https://www.fmz.com/strategy/17289)
|
||||
- [币安永续多币种对冲策略原版(做多超跌做空超涨)4月13日最新止损模块](https://www.fmz.com/strategy/195226)
|
||||
- [okex合约交易测试](https://www.fmz.com/strategy/199102)
|
||||
- [RecordsCollecter (教学)](https://www.fmz.com/strategy/199120)
|
||||
- [多平台对冲稳定套利](https://www.fmz.com/strategy/194150)
|
||||
- [期货做市高频交易策略](https://www.fmz.com/strategy/194180)
|
||||
- [币安永续多币种对冲策略(做空或做多山寨币指数)4月10日改进Bug,需要更新](https://www.fmz.com/strategy/194825)
|
||||
- [用FMZ的均线的交易方式PYTHON版本2](https://www.fmz.com/strategy/198397)
|
||||
- [MACD低买高卖自动跟单滑动止损](https://www.fmz.com/strategy/194224)
|
||||
- [一根均线 趋势 Demo](https://www.fmz.com/strategy/193609)
|
||||
- [混合图表的一个复杂例子](https://www.fmz.com/strategy/196963)
|
||||
- [麦语言海龟策略体验](https://www.fmz.com/strategy/126968)
|
||||
- [币安策略二去掉高低分](https://www.fmz.com/strategy/196445)
|
||||
- [测试多图表展示](https://www.fmz.com/strategy/38203)
|
||||
- [简单的股票趋势跟踪交易策略](https://www.fmz.com/strategy/195935)
|
||||
- [合约下单](https://www.fmz.com/strategy/195453)
|
||||
- [网格变形策略之单边网格 (OK期货)](https://www.fmz.com/strategy/8474)
|
||||
- [测试现货买卖操作(分限价、市价两种情况)](https://www.fmz.com/strategy/191349)
|
||||
- [固定间距批量挂单插件](https://www.fmz.com/strategy/190017)
|
||||
- [期货一键平仓插件|Cover all contracts](https://www.fmz.com/strategy/191363)
|
||||
- [交易所上新币第一时间挂单机器人](https://www.fmz.com/strategy/194206)
|
||||
- [C++期货高频套币策略OKEX Websocket版](https://www.fmz.com/strategy/163447)
|
||||
- [一根均线 趋势 Demo](https://www.fmz.com/strategy/193609)
|
||||
- [8.13 R-Breaker交易策略](https://www.fmz.com/strategy/187009)
|
||||
- [CTP商品期货多品种均线策略](https://www.fmz.com/strategy/193043)
|
||||
- [割庄神器](https://www.fmz.com/strategy/191622)
|
||||
@@ -183,12 +200,10 @@
|
||||
- [海龟](https://www.fmz.com/strategy/128382)
|
||||
- [OKEX期现对冲](https://www.fmz.com/strategy/127939)
|
||||
- [现现对冲(搬砖)](https://www.fmz.com/strategy/127154)
|
||||
- [测试多图表展示](https://www.fmz.com/strategy/38203)
|
||||
- [选择性套利](https://www.fmz.com/strategy/134371)
|
||||
- [MACD+MA指标组合策略](https://www.fmz.com/strategy/128134)
|
||||
- [(学习)API与教程内代码学习文件](https://www.fmz.com/strategy/131864)
|
||||
- [封装Bithumb的ordersDetail接口](https://www.fmz.com/strategy/132241)
|
||||
- [麦语言海龟策略体验](https://www.fmz.com/strategy/126968)
|
||||
- [Bitmex仓位变化推送微信(wss协议,需要bitmex api ID)|Bitmex position-change push(websocket)](https://www.fmz.com/strategy/128624)
|
||||
- [基于ATR波动率指标构建的通道策略|Channel strategy based on ATR volatility indicator](https://www.fmz.com/strategy/127691)
|
||||
- [升级版恒温器策略|Upgraded Thermostat Strategy](https://www.fmz.com/strategy/129086)
|
||||
@@ -254,7 +269,6 @@
|
||||
- [计划委托卖出](https://www.fmz.com/strategy/747)
|
||||
- [单点狙击高频加仓自动反手解套算法 V1.2](https://www.fmz.com/strategy/2406)
|
||||
- [抢盘口做市策略 - 高频逼近型](https://www.fmz.com/strategy/348)
|
||||
- [网格变形策略之单边网格 (OK期货)](https://www.fmz.com/strategy/8474)
|
||||
- [测试-CTP商品期货连接状态 (图表显示)](https://www.fmz.com/strategy/22838)
|
||||
- [图表模板(增加状态栏表格)](https://www.fmz.com/strategy/20967)
|
||||
- [测试-布林带策略(基于策略框架)](https://www.fmz.com/strategy/28128)
|
||||
@@ -274,7 +288,6 @@
|
||||
- [多平台强制滑动平仓(买一价)](https://www.fmz.com/strategy/99)
|
||||
- [定点进场下单定点离场卖单](https://www.fmz.com/strategy/64)
|
||||
- [【001】单平台均衡策略 【研究】](https://www.fmz.com/strategy/82325)
|
||||
- [CTP商品期货多品种海龟交易策略](https://www.fmz.com/strategy/17289)
|
||||
- [python版 Dual Thrust OKCoin 期货](https://www.fmz.com/strategy/21856)
|
||||
- [五线向上&&突破前高](https://www.fmz.com/strategy/76827)
|
||||
- [C++ API调用例子](https://www.fmz.com/strategy/61533)
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
RecordsCollecter (教学)
|
||||
|
||||
> 策略作者
|
||||
|
||||
小小梦
|
||||
|
||||
> 策略描述
|
||||
|
||||
相关文章:https://www.fmz.com/bbs-topic/5425
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|dropNames|[]|删除表的名称|
|
||||
|
||||
|
||||
> 源码 (python)
|
||||
|
||||
``` python
|
||||
import pymongo
|
||||
import json
|
||||
|
||||
def main():
|
||||
Log("测试数据收集")
|
||||
|
||||
# 连接数据库服务
|
||||
myDBClient = pymongo.MongoClient("mongodb://localhost:27017") # mongodb://127.0.0.1:27017
|
||||
# 创建数据库
|
||||
huobi_DB = myDBClient["huobi"]
|
||||
|
||||
# 打印目前数据库表
|
||||
collist = huobi_DB.list_collection_names()
|
||||
Log("collist:", collist)
|
||||
|
||||
# 检测是否删除表
|
||||
arrDropNames = json.loads(dropNames)
|
||||
if isinstance(arrDropNames, list):
|
||||
for i in range(len(arrDropNames)):
|
||||
dropName = arrDropNames[i]
|
||||
if isinstance(dropName, str):
|
||||
if not dropName in collist:
|
||||
continue
|
||||
tab = huobi_DB[dropName]
|
||||
Log("dropName:", dropName, "删除:", dropName)
|
||||
ret = tab.drop()
|
||||
collist = huobi_DB.list_collection_names()
|
||||
if dropName in collist:
|
||||
Log(dropName, "删除失败")
|
||||
else :
|
||||
Log(dropName, "删除成功")
|
||||
|
||||
# 创建records表
|
||||
huobi_DB_Records = huobi_DB["records"]
|
||||
|
||||
# 请求数据
|
||||
preBarTime = 0
|
||||
index = 1
|
||||
while True:
|
||||
r = _C(exchange.GetRecords)
|
||||
if len(r) < 2:
|
||||
Sleep(1000)
|
||||
continue
|
||||
if preBarTime == 0:
|
||||
# 首次写入所有BAR数据
|
||||
for i in range(len(r) - 1):
|
||||
# 逐根写入
|
||||
bar = r[i]
|
||||
huobi_DB_Records.insert_one({"index": index, "High": bar["High"], "Low": bar["Low"], "Open": bar["Open"], "Close": bar["Close"], "Time": bar["Time"], "Volume": bar["Volume"]})
|
||||
index += 1
|
||||
preBarTime = r[-1]["Time"]
|
||||
elif preBarTime != r[-1]["Time"]:
|
||||
bar = r[-2]
|
||||
huobi_DB_Records.insert_one({"index": index, "High": bar["High"], "Low": bar["Low"], "Open": bar["Open"], "Close": bar["Close"], "Time": bar["Time"], "Volume": bar["Volume"]})
|
||||
index += 1
|
||||
preBarTime = r[-1]["Time"]
|
||||
LogStatus(_D(), "preBarTime:", preBarTime, "_D(preBarTime):", _D(preBarTime/1000), "index:", index)
|
||||
Sleep(10000)
|
||||
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/199120
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-16 15:18:28
|
||||
-253
@@ -1,253 +0,0 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
fcoin-多空力量排序
|
||||
|
||||
> 策略作者
|
||||
|
||||
小天才收割机
|
||||
|
||||
> 策略描述
|
||||
|
||||
fcoin挖矿策略
|
||||
盘口多空比排序挖矿
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Interval|1000|出错重试间隔(毫秒)|
|
||||
|LoopInterval|0.1|轮询间隔(秒)|
|
||||
|Step|0.1|网格间隔(元)|
|
||||
|Lot|0.005|手数|
|
||||
|MaxNets|6|最大网格数|
|
||||
|DisableLog|false|关闭订单跟踪|
|
||||
|MinStock|0.01|最小交易币数|
|
||||
|amountPrecision|4|数量精度|
|
||||
|pricePrecision|true|价格精度|
|
||||
|level|true|计算均价深度|
|
||||
|BUY_START_LEVEL|5|买单起始深度|
|
||||
|SELL_START_LEVEL|5|卖单起始深度|
|
||||
|ratioThresh|2|挂单量比值阈值|
|
||||
|DepthLevel|6|统计深度|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
|
||||
function CancelPendingOrders() {
|
||||
var orders = _C(exchange.GetOrders);
|
||||
for (var j = 0; j < orders.length; j++) {
|
||||
exchange.CancelOrder(orders[j].Id, orders[j]);
|
||||
}
|
||||
}
|
||||
|
||||
function onexit() {
|
||||
CancelPendingOrders();
|
||||
Sleep(100);
|
||||
Log("exit");
|
||||
}
|
||||
|
||||
var initPrice = _C(exchange.GetTicker).Buy;
|
||||
var currency = _C(exchange.GetCurrency);
|
||||
|
||||
if(currency == "BTC_USDT") {
|
||||
MinStock = 0.005;
|
||||
amountPrecision = 4;
|
||||
pricePrecision = 1;
|
||||
} else if(currency == "ETH_USDT") {
|
||||
MinStock = 0.05;
|
||||
amountPrecision = 4;
|
||||
pricePrecision = 2;
|
||||
} else if(currency == "LTC_USDT") {
|
||||
MinStock = 0.5;
|
||||
amountPrecision = 4;
|
||||
pricePrecision = 2;
|
||||
} else if(currency == "EOS_USDT") {
|
||||
MinStock = 1;
|
||||
amountPrecision = 4;
|
||||
pricePrecision = 3;
|
||||
} else if(currency == "XRP_USDT") {
|
||||
MinStock = 10;
|
||||
amountPrecision = 2;
|
||||
pricePrecision = 4;
|
||||
} else if(currency == "BCH_USDT") {
|
||||
MinStock = 0.05;
|
||||
amountPrecision = 4;
|
||||
pricePrecision = 1;
|
||||
}
|
||||
|
||||
Step = 1 / Math.pow(10, pricePrecision);
|
||||
MaxNets = 50;
|
||||
|
||||
function updateProfit(accountInit, accountNow, ticker) {
|
||||
var netNow = accountNow.Balance + accountNow.FrozenBalance + ((accountNow.Stocks + accountNow.FrozenStocks) * ticker.Buy);
|
||||
var netInit = accountInit.Balance + accountInit.FrozenBalance + ((accountInit.Stocks + accountInit.FrozenStocks) * initPrice);
|
||||
LogProfit(_N(netNow - netInit, 2));
|
||||
}
|
||||
|
||||
var LastOrdersLength = null;
|
||||
|
||||
var initAccount = _C(exchange.GetAccount);
|
||||
var initPrice = _C(exchange.GetTicker).Last;
|
||||
var initInUSD = _N(initAccount.Stocks * initPrice + initAccount.Balance, 2);
|
||||
|
||||
function calcDepth(orders) {
|
||||
var base = parseInt(orders[0].Price);
|
||||
var allAmount = 0;
|
||||
var n = 0;
|
||||
for (var i = 0; i < orders.length && n < DepthLevel; i++) {
|
||||
var p = parseInt(orders[i].Price);
|
||||
if (p != base) {
|
||||
n++;
|
||||
base = p;
|
||||
}
|
||||
allAmount += orders[i].Amount;
|
||||
}
|
||||
return allAmount;
|
||||
}
|
||||
|
||||
function onTick() {
|
||||
var ticker = _C(exchange.GetTicker);
|
||||
var account = _C(exchange.GetAccount);
|
||||
var orders = _C(exchange.GetOrders);
|
||||
var depth = _C(exchange.GetDepth);
|
||||
if (LastOrdersLength != null && LastOrdersLength != orders.length) {
|
||||
updateProfit(initAccount, account, ticker);
|
||||
}
|
||||
LastOrdersLength = orders.length;
|
||||
|
||||
var currentInUSD = _N(account.Stocks * ticker.Last + account.Balance, 2);
|
||||
LogProfit(currentInUSD - initInUSD, "&");
|
||||
|
||||
var msg = "";
|
||||
msg += "lastPrice = " + ticker.Last + "\n" +
|
||||
"初始币:" + initAccount.Stocks + ",初始钱:" + initAccount.Balance + ", initUSD:" + initInUSD + "\n" +
|
||||
"当前币:" + account.Stocks + ",当前钱:" + account.Balance + ", currentUSD:" + currentInUSD;
|
||||
|
||||
// 多空力量对比
|
||||
var asksAmount = calcDepth(depth.Asks);
|
||||
var bidsAmount = calcDepth(depth.Bids);
|
||||
var ratio = Math.max(asksAmount/bidsAmount, bidsAmount/asksAmount);
|
||||
ratio = asksAmount > bidsAmount ? ratio : -ratio;
|
||||
var buy_start_level = BUY_START_LEVEL;
|
||||
var sell_start_level = SELL_START_LEVEL;
|
||||
if(ratio > ratioThresh) {
|
||||
buy_start_level = 1;
|
||||
sell_start_level = 6;
|
||||
} else if(ratio <= -2) {
|
||||
sell_start_level = 1;
|
||||
buy_start_level = 6;
|
||||
}
|
||||
|
||||
//var buy = depth.Bids[level].Price;
|
||||
//var sell = depth.Asks[level].Price;
|
||||
// 盘口上下加权平均
|
||||
var avg_buy = 0;
|
||||
var buy_amount = 0;
|
||||
var avg_sell = 0;
|
||||
var sell_amount = 0;
|
||||
for(var i = 1; i <= level; ++i) {
|
||||
avg_buy += depth.Bids[i - 1].Price * depth.Bids[i - 1].Amount;
|
||||
buy_amount += depth.Bids[i - 1].Amount;
|
||||
avg_sell += depth.Asks[i - 1].Price * depth.Asks[i - 1].Amount;
|
||||
sell_amount += depth.Asks[i - 1].Amount;
|
||||
}
|
||||
var buy = _N(avg_buy / buy_amount, pricePrecision);
|
||||
var sell = _N(avg_sell / sell_amount, pricePrecision);
|
||||
|
||||
//var mid = adjustFloat(ticker.Buy + ((ticker.Sell - ticker.Buy) / 2));
|
||||
var mid = _N(buy + ((sell - buy) / 2), pricePrecision);
|
||||
// var numBuy = parseInt(Math.min(MaxNets / 2 , (mid - buy) / Step, account.Balance / buy / Lot));
|
||||
var numBuy = parseInt(Math.min(MaxNets / 2, account.Balance / buy / Lot));
|
||||
var numSell = parseInt(Math.min(MaxNets / 2, account.Stocks / Lot));
|
||||
var num = Math.max(numBuy, numSell);
|
||||
|
||||
msg += "\nticker.sell = " + ticker.Sell + ", ticker.buy = " + ticker.Buy +
|
||||
", mid = " + _N((ticker.Buy + ticker.Sell) / 2, pricePrecision) + "\n" +
|
||||
"avg_sell = " + sell + ", avg_buy = " + buy + ", mid = " + mid;
|
||||
msg += "\n多/空 = " + ratio;
|
||||
LogStatus(msg);
|
||||
|
||||
var ordersKeep = [];
|
||||
var queue = [];
|
||||
for(var i = buy_start_level; i < numBuy + buy_start_level; ++i) {
|
||||
var buyPrice = _N(mid - (i * Step), pricePrecision);
|
||||
var alreadyBuy = false;
|
||||
for (j = 0; j < orders.length; j++) {
|
||||
if (orders[j].Type == ORDER_TYPE_BUY) {
|
||||
if (Math.abs(orders[j].Price - buyPrice) < (Step / 2)) {
|
||||
alreadyBuy = true;
|
||||
ordersKeep.push(orders[j].Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((!alreadyBuy) && (i < numBuy)) {
|
||||
queue.push([buyPrice, ORDER_TYPE_BUY]);
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = sell_start_level; i < numSell + sell_start_level; ++i) {
|
||||
var sellPrice = _N(mid + (i * Step), pricePrecision);
|
||||
var alreadySell = false;
|
||||
for (j = 0; j < orders.length; j++) {
|
||||
if (orders[j].Type == ORDER_TYPE_SELL) {
|
||||
if (Math.abs(orders[j].Price - sellPrice) < (Step / 2)) {
|
||||
alreadySell = true;
|
||||
ordersKeep.push(orders[j].Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((!alreadySell) && (i < numSell)) {
|
||||
queue.push([sellPrice, ORDER_TYPE_SELL]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < orders.length; i++) {
|
||||
var keep = false;
|
||||
for (var j = 0; j < ordersKeep.length; j++) {
|
||||
if (orders[i].Id == ordersKeep[j]) {
|
||||
keep = true;
|
||||
}
|
||||
}
|
||||
if (!keep) {
|
||||
exchange.CancelOrder(orders[i].Id);
|
||||
LastOrdersLength--;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < queue.length; i++) {
|
||||
if (queue[i][1] == ORDER_TYPE_BUY) {
|
||||
exchange.Buy(_N(queue[i][0], pricePrecision), Lot);
|
||||
} else {
|
||||
exchange.Sell(_N(queue[i][0], pricePrecision), Lot);
|
||||
}
|
||||
LastOrdersLength++;
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (DisableLog) {
|
||||
EnableLog(false);
|
||||
}
|
||||
Log(initAccount);
|
||||
LoopInterval = Math.max(LoopInterval, 1);
|
||||
// Lot = Math.max(MinStock, Lot);
|
||||
Lot = MinStock;
|
||||
while (true) {
|
||||
onTick();
|
||||
Sleep(LoopInterval * 1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/161034
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-12-26 11:24:50
|
||||
@@ -1,66 +0,0 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
fcoin挖矿演示
|
||||
|
||||
> 策略作者
|
||||
|
||||
张超
|
||||
|
||||
> 策略描述
|
||||
|
||||
看到官方api群里开源的python版本,虽然很简单了,但是还有很多人架不起来环境
|
||||
出问题的原因也有很多种,不太适合新手
|
||||
本着开源共享的精神,我也开源一份,请大家斧正
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Amount|10|下单量|
|
||||
|Wait|true|等待间隔(秒)|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
function DealOrder(orderType, sellPrice) {
|
||||
var ticker = _C(exchange.GetTicker);
|
||||
var action = orderType == ORDER_TYPE_BUY ? 'Buy' : 'Sell';
|
||||
_C(exchange[action], (orderType == ORDER_TYPE_SELL ? sellPrice : ticker.Last), Amount);
|
||||
var recentPrice = ticker.Last;
|
||||
while (true) {
|
||||
Sleep(Wait*1000);
|
||||
var orders = _C(exchange.GetOrders);
|
||||
if (orders == 0) {
|
||||
break;
|
||||
}
|
||||
ticker = _C(exchange.GetTicker);
|
||||
_.each(orders, function(o) {
|
||||
if (o.DealAmount == 0 && ((o.Type == ORDER_TYPE_SELL && ticker.Last >= sellPrice && ticker.Last != recentPrice) || (ticker.Last != recentPrice && o.Type == ORDER_TYPE_BUY))) {
|
||||
exchange.CancelOrder(o.Id);
|
||||
recentPrice = ticker.Last
|
||||
_C(exchange[action], ticker.Last, Amount);
|
||||
}
|
||||
});
|
||||
}
|
||||
return recentPrice;
|
||||
}
|
||||
function main() {
|
||||
while (true) {
|
||||
DealOrder(ORDER_TYPE_SELL, DealOrder(ORDER_TYPE_BUY));
|
||||
Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/97791
|
||||
|
||||
> 更新时间
|
||||
|
||||
2018-06-12 10:28:01
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
okex合约交易测试
|
||||
|
||||
> 策略作者
|
||||
|
||||
a838899
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> 源码 (python)
|
||||
|
||||
``` python
|
||||
'''backtest
|
||||
start: 2019-07-22 00:00:00
|
||||
end: 2020-03-29 00:00:00
|
||||
period: 1d
|
||||
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
|
||||
args: [["StopLossRatio",0.1],["Interval",5]]
|
||||
'''
|
||||
def main():
|
||||
#Log(exchange.GetAccount())
|
||||
exchange.SetContractType("this_week")
|
||||
exchange.SetMarginLevel(10)
|
||||
# 开多单,买币
|
||||
exchange.SetDirection("buy")
|
||||
exchange.Buy(20000,1)
|
||||
position = _C(exchange.GetPosition)
|
||||
Log(position)
|
||||
# 平多单,卖币
|
||||
exchange.SetDirection("closebuy")
|
||||
exchange.Sell(10000,1)
|
||||
Log(position)
|
||||
# 开空单,卖币
|
||||
exchange.SetDirection("sell")
|
||||
exchange.Sell(10000,1)
|
||||
Log(position)
|
||||
# 平空单,买币
|
||||
exchange.SetDirection("closesell")
|
||||
exchange.Buy(20000,1)
|
||||
Log(position)
|
||||
Log("Amount:", position[0].Amount, "FrozenAmount:", position[0].FrozenAmount,"Price:",
|
||||
position[0].Price, "Profit:", position[0].Profit,"Type:", position[0].Type, "ContractType:", position[0].ContractType)
|
||||
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/199102
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-16 16:37:56
|
||||
+1
-1
@@ -103,4 +103,4 @@ https://www.fmz.com/strategy/193609
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-03-28 20:56:30
|
||||
2020-04-11 21:28:30
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
合约下单
|
||||
|
||||
> 策略作者
|
||||
|
||||
一拳男孩
|
||||
|
||||
> 策略描述
|
||||
|
||||
### $.openPos(buy|sell, amount);
|
||||
开仓
|
||||
### $.closePos(buy|sell, amount);
|
||||
平仓
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|SLIP_POINT|0.01|滑点|
|
||||
|TRADE_TIMEOUT|10|交易超时|
|
||||
|FACE_VALUE|10|面值|
|
||||
|PRICE_PRECISION|4|价格精度|
|
||||
|AMOUNT_PRECISION|8|交易量精度|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
/*backtest
|
||||
start: 2020-01-08 00:00:00
|
||||
end: 2020-04-06 00:00:00
|
||||
period: 1d
|
||||
exchanges: [{"eid":"Futures_OKCoin","currency":"ETH_USD","stocks":1400}]
|
||||
args: [["PRICE_PRECISION",2],["AMOUNT_PRECISION",4]]
|
||||
*/
|
||||
|
||||
var tempOrders = [];
|
||||
|
||||
var COLOR = {
|
||||
GREEN: '#00a500',
|
||||
RED: '#ff0000',
|
||||
GRAY: '#b2b2b2',
|
||||
ORANGE: '#ff9e38'
|
||||
}
|
||||
|
||||
function cancelOrder(ex,order_id){
|
||||
if (!order_id) return Log('不正确的订单id', order_id);
|
||||
Log("取消订单" + order_id);
|
||||
ex.CancelOrder(order_id);
|
||||
Sleep(500);
|
||||
var orders=_C(ex.GetOrders);
|
||||
var find=false;
|
||||
for (var i=0;i<orders.length;++i){
|
||||
if (orders[i].Id===order_id){
|
||||
find=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (find){
|
||||
cancelOrder(ex.order_id);
|
||||
}
|
||||
}
|
||||
|
||||
var trade = function(direction, amount) {
|
||||
var ticker = _C(exchange.GetTicker);
|
||||
exchange.SetDirection(direction);
|
||||
var price = 0
|
||||
if (_.contains(['buy', 'closesell'], direction)) {
|
||||
price = _N(new Decimal(ticker.Last).plus(SLIP_POINT).toNumber(), PRICE_PRECISION);
|
||||
} else {
|
||||
price = _N(new Decimal(ticker.Last).minus(SLIP_POINT).toNumber(), PRICE_PRECISION);
|
||||
}
|
||||
var orderInfo = null;
|
||||
var avgPrice = null;
|
||||
var oid = null;
|
||||
var beginTime = null;
|
||||
var dealAmount = _.reduce(tempOrders, function(p, n) {
|
||||
return new Decimal(p).plus(n.DealAmount).toNumber();
|
||||
}, 0);
|
||||
var remainAmount = Math.floor(new Decimal(amount).minus(dealAmount).toNumber());
|
||||
if (remainAmount < 1) {
|
||||
avgPrice = _N(new Decimal(_.reduce(tempOrders, function(p, n) {
|
||||
return new Decimal(p).plus(new Decimal(n.AvgPrice).mul(n.DealAmount).mul(FACE_VALUE)).toNumber()
|
||||
}, 0)).div(new Decimal(dealAmount).mul(FACE_VALUE)).toNumber(), PRICE_PRECISION);
|
||||
orderInfo = {
|
||||
Id: _.last(tempOrders).Id,
|
||||
Type: _.contains(['buy', 'closebuy'], direction) ? PD_LONG : PD_SHORT,
|
||||
Price: price,
|
||||
AvgPrice: avgPrice,
|
||||
Amount: amount,
|
||||
DealAmount: dealAmount,
|
||||
Orders: _.pluck(tempOrders, 'Id')
|
||||
}
|
||||
tempOrders = [];
|
||||
return orderInfo;
|
||||
} else {
|
||||
oid = _.contains(['buy', 'closesell'], direction) ? exchange.Buy(price, remainAmount) : exchange.Sell(price, remainAmount);
|
||||
if (oid) {
|
||||
beginTime = Unix();
|
||||
while(1) {
|
||||
orderInfo = _C(exchange.GetOrder, oid);
|
||||
if (orderInfo.Status !== ORDER_STATE_PENDING) {
|
||||
tempOrders.push(orderInfo);
|
||||
return trade(direction, amount);
|
||||
}
|
||||
if (Unix() - beginTime >= TRADE_TIMEOUT) {
|
||||
Log('交易超时, 撤销订单' + oid + COLOR.RED);
|
||||
cancelOrder(exchange, oid);
|
||||
continue;
|
||||
}
|
||||
Sleep(250);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
$.OpenPos = function(direction, amount) {
|
||||
return trade(direction, amount);
|
||||
};
|
||||
|
||||
$.ClosePos = function(direction, amount) {
|
||||
return trade('close' + direction, amount);
|
||||
}
|
||||
|
||||
function main() {
|
||||
exchange.SetContractType('this_week');
|
||||
exchange.SetPrecision(PRICE_PRECISION, 0);
|
||||
Log($.OpenPos('buy', 2));
|
||||
$.OpenPos('sell', 1);
|
||||
$.ClosePos('buy', 1);
|
||||
$.ClosePos('buy', 1);
|
||||
$.ClosePos('sell', 1);
|
||||
Log(exchange.GetPosition());
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/195453
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-07 01:25:52
|
||||
@@ -0,0 +1,486 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
商品期货多品种主力自动移仓海龟交易策略
|
||||
|
||||
> 策略作者
|
||||
|
||||
Zero
|
||||
|
||||
> 策略描述
|
||||
|
||||
> 功能
|
||||
|
||||
- 主力连续或者指数合约自动移仓换月
|
||||
- 支持自动或手动恢复进度
|
||||
- 可同时操作多个不同品种
|
||||
- 增加时间段区分与各种网络错误问题的应对处理
|
||||
|
||||
> 运行状态
|
||||
|
||||

|
||||
|
||||
> 回测示例
|
||||
|
||||

|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Instruments|MA888,pp888,v888,rb888,jm888|合约列表|
|
||||
|CustomBalance|false|定义启动资金|
|
||||
|InitBalance|false|启动资金|
|
||||
|LoopInterval|3|轮询周期(秒)|
|
||||
|RiskRatio|true|% Risk Per N ( 0 - 100)|
|
||||
|ATRLength|20|ATR计算周期|
|
||||
|EnterPeriodA|20|系统一入市周期|
|
||||
|LeavePeriodA|10|系统一离市周期|
|
||||
|EnterPeriodB|55|系统二入市周期|
|
||||
|LeavePeriodB|20|系统二离市周期|
|
||||
|UseEnterFilter|true|使用入市过滤|
|
||||
|IncSpace|0.5|加仓间隔(N的倍数)|
|
||||
|StopLossRatio|2|止损系数(N的倍数)|
|
||||
|MaxLots|4|单品种加仓次数|
|
||||
|RMode|0|进度恢复模式: 自动|手动|
|
||||
|VMStatus|{}|手动恢复字符串|
|
||||
|WXPush|true|推送交易信息|
|
||||
|MaxTaskRetry|5|开仓最多重试次数|
|
||||
|KeepRatio|20|预留保证金比例|
|
||||
|
||||
|
||||
|
||||
|
||||
|按钮|默认值|描述|
|
||||
|----|----|----|
|
||||
|暂停/继续|__button__|暂停/继续|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
/*backtest
|
||||
start: 2016-05-01 00:00:00
|
||||
end: 2020-02-15 23:59:00
|
||||
period: 1d
|
||||
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
|
||||
*/
|
||||
|
||||
var _q = $.NewTaskQueue();
|
||||
|
||||
var TTManager = {
|
||||
New: function(needRestore, symbol, initBalance, keepBalance, riskRatio, atrLen, enterPeriodA, leavePeriodA, enterPeriodB, leavePeriodB, useFilter,
|
||||
multiplierN, multiplierS, maxLots) {
|
||||
|
||||
// subscribe
|
||||
var symbolDetail = _C(exchange.SetContractType, symbol);
|
||||
if (symbolDetail.VolumeMultiple == 0 || symbolDetail.MaxLimitOrderVolume == 0 || symbolDetail.MinLimitOrderVolume == 0 || symbolDetail.LongMarginRatio == 0 || symbolDetail.ShortMarginRatio == 0) {
|
||||
Log(symbolDetail);
|
||||
throw "合约信息异常";
|
||||
} else {
|
||||
Log("合约", symbolDetail.InstrumentName, "一手", symbolDetail.VolumeMultiple, "份, 最大下单量", symbolDetail.MaxLimitOrderVolume, "保证金率:", _N(symbolDetail.LongMarginRatio), _N(symbolDetail.ShortMarginRatio), "交割日期", symbolDetail.StartDelivDate);
|
||||
}
|
||||
|
||||
var obj = {
|
||||
symbol: symbol,
|
||||
tradeSymbol: symbolDetail.InstrumentID,
|
||||
initBalance: initBalance,
|
||||
keepBalance: keepBalance,
|
||||
riskRatio: riskRatio,
|
||||
atrLen: atrLen,
|
||||
enterPeriodA: enterPeriodA,
|
||||
leavePeriodA: leavePeriodA,
|
||||
enterPeriodB: enterPeriodB,
|
||||
leavePeriodB: leavePeriodB,
|
||||
useFilter: useFilter,
|
||||
multiplierN: multiplierN,
|
||||
multiplierS: multiplierS
|
||||
};
|
||||
obj.maxLots = maxLots;
|
||||
obj.lastPrice = 0;
|
||||
obj.symbolDetail = symbolDetail;
|
||||
obj.status = {
|
||||
symbol: symbol,
|
||||
recordsLen: 0,
|
||||
vm: [],
|
||||
open: 0,
|
||||
cover: 0,
|
||||
st: 0,
|
||||
marketPosition: 0,
|
||||
lastPrice: 0,
|
||||
holdPrice: 0,
|
||||
holdAmount: 0,
|
||||
holdProfit: 0,
|
||||
switchCount: 0,
|
||||
N: 0,
|
||||
upLine: 0,
|
||||
downLine: 0,
|
||||
lastErr: "",
|
||||
lastErrTime: "",
|
||||
stopPrice: '',
|
||||
leavePrice: '',
|
||||
isTrading: false
|
||||
};
|
||||
|
||||
obj.setLastError = function(err) {
|
||||
if (typeof(err) === 'undefined' || err === '') {
|
||||
obj.status.lastErr = "";
|
||||
obj.status.lastErrTime = "";
|
||||
return;
|
||||
}
|
||||
var t = new Date();
|
||||
obj.status.lastErr = err;
|
||||
obj.status.lastErrTime = t.toLocaleString();
|
||||
};
|
||||
obj.reset = function(marketPosition, openPrice, N, leavePeriod, preBreakoutFailure) {
|
||||
if (typeof(marketPosition) !== 'undefined') {
|
||||
obj.marketPosition = marketPosition;
|
||||
obj.openPrice = openPrice;
|
||||
obj.preBreakoutFailure = preBreakoutFailure;
|
||||
obj.N = N;
|
||||
obj.leavePeriod = leavePeriod;
|
||||
var pos = _q.GetPosition(exchange, obj.tradeSymbol, marketPosition > 0 ? PD_LONG : PD_SHORT);
|
||||
if (pos) {
|
||||
obj.holdPrice = pos.Price;
|
||||
obj.holdAmount = pos.Amount;
|
||||
Log(obj.symbol, "仓位", pos);
|
||||
} else {
|
||||
throw "恢复" + obj.symbol + "的持仓状态出错, 没有找到仓位信息";
|
||||
}
|
||||
Log("恢复", obj.symbol, "加仓次数", obj.marketPosition, "持仓均价:", obj.holdPrice, "持仓数量:", obj.holdAmount, "最后一次加仓价", obj.openPrice, "N值", obj.N, "离市周期:", leavePeriod, "上次突破:", obj.preBreakoutFailure ? "失败" : "成功");
|
||||
obj.status.open = 1;
|
||||
obj.status.vm = [obj.marketPosition, obj.openPrice, obj.N, obj.leavePeriod, obj.preBreakoutFailure];
|
||||
} else {
|
||||
obj.marketPosition = 0;
|
||||
obj.holdPrice = 0;
|
||||
obj.openPrice = 0;
|
||||
obj.holdAmount = 0;
|
||||
obj.holdProfit = 0;
|
||||
obj.preBreakoutFailure = true; // test system A
|
||||
obj.N = 0;
|
||||
obj.leavePeriod = leavePeriodA;
|
||||
}
|
||||
obj.holdProfit = 0;
|
||||
obj.lastErr = "";
|
||||
obj.lastErrTime = "";
|
||||
};
|
||||
|
||||
obj.Status = function() {
|
||||
obj.status.N = obj.N;
|
||||
obj.status.marketPosition = obj.marketPosition;
|
||||
obj.status.holdPrice = obj.holdPrice;
|
||||
obj.status.holdAmount = obj.holdAmount;
|
||||
obj.status.lastPrice = obj.lastPrice;
|
||||
if (obj.lastPrice > 0 && obj.holdAmount > 0 && obj.marketPosition !== 0) {
|
||||
obj.status.holdProfit = _N((obj.lastPrice - obj.holdPrice) * obj.holdAmount * obj.symbolDetail.VolumeMultiple, 4) * (obj.marketPosition > 0 ? 1 : -1);
|
||||
} else {
|
||||
obj.status.holdProfit = 0;
|
||||
}
|
||||
obj.status.symbolDetail = obj.symbolDetail;
|
||||
return obj.status;
|
||||
};
|
||||
obj.Poll = function() {
|
||||
obj.status.isTrading = $.IsTrading(obj.symbol);
|
||||
if (!obj.status.isTrading) {
|
||||
return;
|
||||
}
|
||||
// busy
|
||||
if (_q.hasTask(obj.tradeSymbol)) {
|
||||
return
|
||||
}
|
||||
// Loop
|
||||
var suffix = WXPush ? '@' : '';
|
||||
// switch symbol
|
||||
var insDetail = exchange.SetContractType(obj.symbol);
|
||||
if (!insDetail) {
|
||||
return
|
||||
}
|
||||
var records = exchange.GetRecords();
|
||||
if (!records) {
|
||||
obj.setLastError("获取K线失败");
|
||||
return;
|
||||
}
|
||||
// update tradeSymbol
|
||||
var tradeSymbol = insDetail.InstrumentID;
|
||||
if (tradeSymbol != obj.tradeSymbol) {
|
||||
var oldSymbol = obj.tradeSymbol;
|
||||
var pos = _q.GetPosition(exchange, oldSymbol);
|
||||
if (pos && pos.Amount > 0) {
|
||||
Log("开始移仓", oldSymbol, "->", tradeSymbol, "数量:", pos.Amount, "#ff0000");
|
||||
obj.status.switchCount++;
|
||||
_q.pushTask(exchange, oldSymbol, (pos.Type == PD_LONG ? "closebuy" : "closesell"), pos.Amount, function(task, ret) {
|
||||
if (!ret) {
|
||||
Log(oldSymbol, "移仓平仓失败 #ff0000");
|
||||
return;
|
||||
}
|
||||
Log("移仓进度平仓成功, 开始开仓", oldSymbol, "->", tradeSymbol, "数量:", pos.Amount, "#0000ff");
|
||||
obj.tradeSymbol = tradeSymbol;
|
||||
obj.symbolDetail = insDetail;
|
||||
_q.pushTask(exchange, tradeSymbol, (pos.Type == PD_LONG ? "buy" : "sell"), pos.Amount, function(task, ret) {
|
||||
if (!ret) {
|
||||
Log(tradeSymbol, "移仓开仓失败, 重置品种进度 #ff0000");
|
||||
obj.marketPosition = 0;
|
||||
return;
|
||||
}
|
||||
Log("移仓成功", oldSymbol, "->", tradeSymbol, "#0000ff");
|
||||
});
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
obj.tradeSymbol = tradeSymbol;
|
||||
obj.symbolDetail = insDetail;
|
||||
}
|
||||
}
|
||||
|
||||
obj.status.recordsLen = records.length;
|
||||
if (records.length < obj.atrLen) {
|
||||
obj.setLastError("K线长度小于 " + obj.atrLen);
|
||||
return;
|
||||
}
|
||||
var opCode = 0; // 0: IDLE, 1: LONG, 2: SHORT, 3: CoverALL
|
||||
var lastPrice = records[records.length - 1].Close;
|
||||
obj.lastPrice = lastPrice;
|
||||
if (obj.marketPosition === 0) {
|
||||
obj.status.stopPrice = '--';
|
||||
obj.status.leavePrice = '--';
|
||||
obj.status.upLine = 0;
|
||||
obj.status.downLine = 0;
|
||||
for (var i = 0; i < 2; i++) {
|
||||
if (i == 0 && obj.useFilter && !obj.preBreakoutFailure) {
|
||||
continue;
|
||||
}
|
||||
var enterPeriod = i == 0 ? obj.enterPeriodA : obj.enterPeriodB;
|
||||
if (records.length < (enterPeriod + 1)) {
|
||||
continue;
|
||||
}
|
||||
var highest = TA.Highest(records, enterPeriod, 'High');
|
||||
var lowest = TA.Lowest(records, enterPeriod, 'Low');
|
||||
obj.status.upLine = obj.status.upLine == 0 ? highest : Math.min(obj.status.upLine, highest);
|
||||
obj.status.downLine = obj.status.downLine == 0 ? lowest : Math.max(obj.status.downLine, lowest);
|
||||
if (lastPrice > highest) {
|
||||
opCode = 1;
|
||||
} else if (lastPrice < lowest) {
|
||||
opCode = 2;
|
||||
}
|
||||
if (opCode != 0) {
|
||||
obj.leavePeriod = (enterPeriod == obj.enterPeriodA) ? obj.leavePeriodA : obj.leavePeriodB;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var spread = obj.marketPosition > 0 ? (obj.openPrice - lastPrice) : (lastPrice - obj.openPrice);
|
||||
obj.status.stopPrice = _N(obj.openPrice + (obj.N * StopLossRatio * (obj.marketPosition > 0 ? -1 : 1)));
|
||||
if (spread > (obj.N * StopLossRatio)) {
|
||||
opCode = 3;
|
||||
obj.preBreakoutFailure = true;
|
||||
Log(obj.symbolDetail.InstrumentName, "止损平仓", suffix);
|
||||
obj.status.st++;
|
||||
} else if (-spread > (IncSpace * obj.N) && Math.abs(obj.marketPosition) < obj.maxLots) {
|
||||
opCode = obj.marketPosition > 0 ? 1 : 2;
|
||||
}
|
||||
if (opCode == 0 && records.length > obj.leavePeriod) {
|
||||
obj.status.leavePrice = obj.marketPosition > 0 ? TA.Lowest(records, obj.leavePeriod, 'Low') : TA.Highest(records, obj.leavePeriod, 'High');
|
||||
if ((obj.marketPosition > 0 && lastPrice < obj.status.leavePrice) ||
|
||||
(obj.marketPosition < 0 && lastPrice > obj.status.leavePrice)) {
|
||||
obj.preBreakoutFailure = false;
|
||||
Log(obj.symbolDetail.InstrumentName, "正常平仓", suffix);
|
||||
opCode = 3;
|
||||
obj.status.cover++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opCode == 0) {
|
||||
return;
|
||||
}
|
||||
if (opCode == 3) {
|
||||
_q.pushTask(exchange, obj.tradeSymbol, "coverall", 0, function(task, ret) {
|
||||
obj.reset();
|
||||
_G(obj.symbol, null);
|
||||
var account = _q.GetAccount(exchange);
|
||||
var accountInfo = JSON.parse(exchange.GetRawJSON());
|
||||
LogProfit(accountInfo.Balance, obj.tradeSymbol, "平仓后权益");
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Open
|
||||
if (Math.abs(obj.marketPosition) >= obj.maxLots) {
|
||||
obj.setLastError("禁止开仓, 超过最大持仓 " + obj.maxLots);
|
||||
return;
|
||||
}
|
||||
var atrs = TA.ATR(records, atrLen);
|
||||
var N = _N(atrs[atrs.length - 1], 4);
|
||||
|
||||
var account = _q.GetAccount(exchange);
|
||||
var unit = parseInt((obj.initBalance-obj.keepBalance) * (obj.riskRatio / 100) / N / obj.symbolDetail.VolumeMultiple);
|
||||
var canOpen = parseInt((account.Balance-obj.keepBalance) / (opCode == 1 ? obj.symbolDetail.LongMarginRatio : obj.symbolDetail.ShortMarginRatio) / (lastPrice * 1.2) / obj.symbolDetail.VolumeMultiple);
|
||||
unit = Math.min(unit, canOpen);
|
||||
if (unit < obj.symbolDetail.MinLimitOrderVolume) {
|
||||
obj.setLastError("可开 " + unit + " 手 无法开仓, " + (canOpen >= obj.symbolDetail.MinLimitOrderVolume ? "风控触发" : "资金限制") + "可用: " + account.Balance);
|
||||
return;
|
||||
}
|
||||
_q.pushTask(exchange, obj.tradeSymbol, (opCode == 1 ? "buy" : "sell"), unit, function(task, ret) {
|
||||
if (!ret) {
|
||||
obj.setLastError("下单失败");
|
||||
return;
|
||||
}
|
||||
Log(obj.symbolDetail.InstrumentName, obj.marketPosition == 0 ? "开仓" : "加仓", "离市周期", obj.leavePeriod, suffix);
|
||||
obj.N = N;
|
||||
obj.openPrice = ret.price;
|
||||
obj.holdPrice = ret.position.Price;
|
||||
if (obj.marketPosition == 0) {
|
||||
obj.status.open++;
|
||||
}
|
||||
obj.holdAmount = ret.position.Amount;
|
||||
obj.marketPosition += opCode == 1 ? 1 : -1;
|
||||
obj.status.vm = [obj.marketPosition, obj.openPrice, N, obj.leavePeriod, obj.preBreakoutFailure];
|
||||
_G(obj.symbol, obj.status.vm);
|
||||
});
|
||||
};
|
||||
var vm = null;
|
||||
if (RMode === 0) {
|
||||
vm = _G(obj.symbol);
|
||||
} else {
|
||||
vm = JSON.parse(VMStatus)[obj.symbol];
|
||||
}
|
||||
if (vm) {
|
||||
Log("准备恢复进度, 当前合约状态为", vm);
|
||||
obj.reset(vm[0], vm[1], vm[2], vm[3], vm[4]);
|
||||
} else {
|
||||
if (needRestore) {
|
||||
Log("没有找到" + obj.symbol + "的进度恢复信息");
|
||||
}
|
||||
obj.reset();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
};
|
||||
|
||||
function onexit() {
|
||||
Log("已退出策略...");
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (exchange.GetName().indexOf('CTP') == -1) {
|
||||
throw "只支持商品期货CTP";
|
||||
}
|
||||
SetErrorFilter("login|ready|流控|连接失败|初始|Timeout");
|
||||
var mode = exchange.IO("mode", 0);
|
||||
if (typeof(mode) !== 'number') {
|
||||
throw "切换模式失败, 请更新到最新托管者!";
|
||||
}
|
||||
while (!exchange.IO("status")) {
|
||||
Sleep(3000);
|
||||
LogStatus("正在等待与交易服务器连接, " + _D());
|
||||
}
|
||||
var positions = _C(exchange.GetPosition);
|
||||
if (positions.length > 0) {
|
||||
Log("检测到当前持有仓位, 系统将开始尝试恢复进度...");
|
||||
Log("持仓信息", positions);
|
||||
}
|
||||
Log("风险系数:", RiskRatio, "N值周期:", ATRLength, "系统1: 入市周期", EnterPeriodA, "离市周期", LeavePeriodA, "系统二: 入市周期", EnterPeriodB, "离市周期", LeavePeriodB, "加仓系数:", IncSpace, "止损系数:", StopLossRatio, "单品种最多开仓:", MaxLots, "次");
|
||||
var initAccount = _q.GetAccount(exchange);
|
||||
var initMargin = JSON.parse(exchange.GetRawJSON()).CurrMargin;
|
||||
var realInitBalance = initAccount.Balance + initMargin;
|
||||
if (CustomBalance) {
|
||||
realInitBalance = InitBalance;
|
||||
Log("自定义启动资产为", realInitBalance);
|
||||
}
|
||||
var keepBalance = _N(realInitBalance * (KeepRatio/100), 3);
|
||||
Log("当前资产信息", initAccount, "保留资金:", keepBalance);
|
||||
|
||||
var tts = [];
|
||||
var filter = [];
|
||||
var arr = Instruments.split(',');
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var symbol = arr[i].replace(/^\s+/g, "").replace(/\s+$/g, "");
|
||||
if (typeof(filter[symbol]) !== 'undefined') {
|
||||
Log(symbol, "已经存在, 系统已自动过滤");
|
||||
continue;
|
||||
}
|
||||
filter[symbol] = true;
|
||||
var hasPosition = false;
|
||||
for (var j = 0; j < positions.length; j++) {
|
||||
if (positions[j].ContractType == symbol) {
|
||||
hasPosition = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var obj = TTManager.New(hasPosition, symbol, realInitBalance, keepBalance, RiskRatio, ATRLength, EnterPeriodA, LeavePeriodA, EnterPeriodB, LeavePeriodB, UseEnterFilter, IncSpace, StopLossRatio, MaxLots);
|
||||
tts.push(obj);
|
||||
}
|
||||
|
||||
|
||||
var lastStatus = '';
|
||||
while (true) {
|
||||
if (GetCommand() === "暂停/继续") {
|
||||
Log("暂停交易中...");
|
||||
while (GetCommand() !== "暂停/继续") {
|
||||
Sleep(1000);
|
||||
}
|
||||
Log("继续交易中...");
|
||||
}
|
||||
while (!exchange.IO("status")) {
|
||||
Sleep(3000);
|
||||
LogStatus("正在等待与交易服务器连接, " + _D() + "\n" + lastStatus);
|
||||
}
|
||||
var tblStatus = {
|
||||
type: "table",
|
||||
title: "持仓信息",
|
||||
cols: ["合约名称", "持仓方向", "持仓均价", "持仓数量", "持仓盈亏", "加仓次数", "开仓次数", "止损次数", "成功次数", "当前价格", "N"],
|
||||
rows: []
|
||||
};
|
||||
var tblMarket = {
|
||||
type: "table",
|
||||
title: "运行状态",
|
||||
cols: ["合约名称", "合约乘数", "保证金率", "交易时间", "移仓次数", "柱线长度", "上线", "下线", "止损价", "离市价", "异常描述", "发生时间"],
|
||||
rows: []
|
||||
};
|
||||
var totalHold = 0;
|
||||
var vmStatus = {};
|
||||
var ts = new Date().getTime();
|
||||
var holdSymbol = 0;
|
||||
for (var i = 0; i < tts.length; i++) {
|
||||
tts[i].Poll();
|
||||
var d = tts[i].Status();
|
||||
if (d.holdAmount > 0) {
|
||||
vmStatus[d.symbol] = d.vm;
|
||||
holdSymbol++;
|
||||
}
|
||||
tblStatus.rows.push([d.symbolDetail.InstrumentID, d.holdAmount == 0 ? '--' : (d.marketPosition > 0 ? '多' : '空'), d.holdPrice, d.holdAmount, d.holdProfit, Math.abs(d.marketPosition), d.open, d.st, d.cover, d.lastPrice, d.N]);
|
||||
tblMarket.rows.push([d.symbolDetail.InstrumentID, d.symbolDetail.VolumeMultiple, _N(d.symbolDetail.LongMarginRatio, 4) + '/' + _N(d.symbolDetail.ShortMarginRatio, 4), (d.isTrading ? '是#0000ff' : '否#ff0000'), d.switchCount, d.recordsLen, d.upLine, d.downLine, d.stopPrice, d.leavePrice, d.lastErr, d.lastErrTime]);
|
||||
totalHold += Math.abs(d.holdAmount);
|
||||
}
|
||||
var now = new Date();
|
||||
var elapsed = now.getTime() - ts;
|
||||
var tblAssets = _q.GetAccount(exchange, true);
|
||||
var nowAccount = _q.Account();
|
||||
|
||||
if (tblAssets.rows.length > 10) {
|
||||
// replace AccountId
|
||||
tblAssets.rows[0] = ["InitAccount", "初始资产", realInitBalance];
|
||||
} else {
|
||||
tblAssets.rows.unshift(["NowAccount", "当前可用", nowAccount], ["InitAccount", "初始资产", realInitBalance]);
|
||||
}
|
||||
if (totalHold > 0) {
|
||||
tblAssets.rows.push(["手动恢复字符串", {body:JSON.stringify(vmStatus), colspan: 2}]);
|
||||
}
|
||||
lastStatus = '`' + JSON.stringify([tblStatus, tblMarket, tblAssets]) + '`\n轮询耗时: ' + elapsed + ' 毫秒, 当前时间: ' + _D() + ', 星期' + ['日', '一', '二', '三', '四', '五', '六'][now.getDay()] + ", 持有品种个数: " + holdSymbol + ", 交易任务: " + _q.size();
|
||||
LogStatus(lastStatus);
|
||||
_q.poll();
|
||||
Sleep(LoopInterval * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/17289
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-17 13:30:40
|
||||
+345
@@ -0,0 +1,345 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
多平台对冲稳定套利
|
||||
|
||||
> 策略作者
|
||||
|
||||
John
|
||||
|
||||
> 策略描述
|
||||
|
||||
程序自动换算汇率,手续费,下单参考买一卖一的价,注意这个不是搬砖,是对冲! 不需要人工转钱或者币,一个存钱,一个存币,一个买,一个卖,高级对冲模式.
|
||||
这种对冲模式不管币的价格是涨还是跌,只赚差价。
|
||||
建仓需要一个放钱,一个放币,比如BTC现在价格6000吧,一个交易所放3000刀,一个放0.45左右得币就行了,或者全部加倍
|
||||
MaxDiff指最低平台差价
|
||||
SlidePrice指下单时的滑动价, 买单就加上这个价, 卖单就减去这个价
|
||||
TickIntervalS 指检测周期
|
||||
跌停值指币跌到这个价格了就不操作,涨停值指币高于这个价格也不操作,防止平台恶意给机器人下套.
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Interval|500|出错重试间隔(毫秒)|
|
||||
|TickInterval|300|检测频率(毫秒)|
|
||||
|MaxDiff|3|最低差价(元)|
|
||||
|SlidePrice|0.1|止损滑动价(元)|
|
||||
|SlideRatio|2|对冲滑动价系数|
|
||||
|StopPriceL|1000|跌停值(元)|
|
||||
|StopPriceH|9000|涨停值(元)|
|
||||
|optFeeTimeout|30|手续费更新周期(分)|
|
||||
|AmountOnce|0.2|单笔交易数量|
|
||||
|UseMarketOrder|false|使用市价单止损|
|
||||
|StopWhenLoss|false|亏损时停止|
|
||||
|MaxLoss|50|最大亏损限度(元)|
|
||||
|SMSAPI|http://|短信通知接口|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
var initState;
|
||||
var isBalance = true;
|
||||
var feeCache = new Array();
|
||||
var feeTimeout = optFeeTimeout * 60000;
|
||||
var lastProfit = 0;
|
||||
var lastAvgPrice = 0;
|
||||
var lastSpread = 0;
|
||||
var lastOpAmount = 0;
|
||||
|
||||
function adjustFloat(v) {
|
||||
return Math.floor(v * 1000) / 1000;
|
||||
}
|
||||
|
||||
function isPriceNormal(v) {
|
||||
return (v >= StopPriceL) && (v <= StopPriceH);
|
||||
}
|
||||
|
||||
function stripTicker(t) {
|
||||
return 'Buy: ' + adjustFloat(t.Buy) + ' Sell: ' + adjustFloat(t.Sell);
|
||||
}
|
||||
|
||||
function updateStatePrice(state) {
|
||||
var now = (new Date()).getTime();
|
||||
for (var i = 0; i < state.details.length; i++) {
|
||||
var ticker = null;
|
||||
var key = state.details[i].exchange.GetName() + state.details[i].exchange.GetCurrency();
|
||||
var fee = null;
|
||||
while (!(ticker = state.details[i].exchange.GetTicker())) {
|
||||
Sleep(Interval);
|
||||
}
|
||||
|
||||
if (key in feeCache) {
|
||||
var v = feeCache[key];
|
||||
if ((now - v.time) > feeTimeout) {
|
||||
delete feeCache[key];
|
||||
} else {
|
||||
fee = v.fee;
|
||||
}
|
||||
}
|
||||
if (!fee) {
|
||||
while (!(fee = state.details[i].exchange.GetFee())) {
|
||||
Sleep(Interval);
|
||||
}
|
||||
feeCache[key] = {
|
||||
fee: fee,
|
||||
time: now
|
||||
};
|
||||
}
|
||||
// Buy-=fee Sell+=fee
|
||||
state.details[i].ticker = {
|
||||
Buy: ticker.Buy * (1 - (fee.Sell / 100)),
|
||||
Sell: ticker.Sell * (1 + (fee.Buy / 100))
|
||||
};
|
||||
state.details[i].realTicker = ticker;
|
||||
state.details[i].fee = fee;
|
||||
}
|
||||
}
|
||||
|
||||
function getProfit(stateInit, stateNow, coinPrice) {
|
||||
var netNow = stateNow.allBalance + (stateNow.allStocks * coinPrice);
|
||||
var netInit = stateInit.allBalance + (stateInit.allStocks * coinPrice);
|
||||
return adjustFloat(netNow - netInit);
|
||||
}
|
||||
|
||||
function getExchangesState() {
|
||||
var allStocks = 0;
|
||||
var allBalance = 0;
|
||||
var minStock = 0;
|
||||
var details = [];
|
||||
for (var i = 0; i < exchanges.length; i++) {
|
||||
var account = null;
|
||||
while (!(account = exchanges[i].GetAccount())) {
|
||||
Sleep(Interval);
|
||||
}
|
||||
allStocks += account.Stocks + account.FrozenStocks;
|
||||
allBalance += account.Balance + account.FrozenBalance;
|
||||
minStock = Math.max(minStock, exchanges[i].GetMinStock());
|
||||
details.push({
|
||||
exchange: exchanges[i],
|
||||
account: account
|
||||
});
|
||||
}
|
||||
return {
|
||||
allStocks: adjustFloat(allStocks),
|
||||
allBalance: adjustFloat(allBalance),
|
||||
minStock: minStock,
|
||||
details: details
|
||||
};
|
||||
}
|
||||
|
||||
function cancelAllOrders() {
|
||||
for (var i = 0; i < exchanges.length; i++) {
|
||||
while (true) {
|
||||
var orders = null;
|
||||
while (!(orders = exchanges[i].GetOrders())) {
|
||||
Sleep(Interval);
|
||||
}
|
||||
|
||||
if (orders.length == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (var j = 0; j < orders.length; j++) {
|
||||
exchanges[i].CancelOrder(orders[j].Id, orders[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function balanceAccounts() {
|
||||
// already balance
|
||||
if (isBalance) {
|
||||
return;
|
||||
}
|
||||
|
||||
cancelAllOrders();
|
||||
|
||||
var state = getExchangesState();
|
||||
var diff = state.allStocks - initState.allStocks;
|
||||
var adjustDiff = adjustFloat(Math.abs(diff));
|
||||
if (adjustDiff < state.minStock) {
|
||||
isBalance = true;
|
||||
} else {
|
||||
Log('初始币总数量:', initState.allStocks, '现在币总数量: ', state.allStocks, '差额:', adjustDiff);
|
||||
// other ways, diff is 0.012, bug A only has 0.006 B only has 0.006, all less then minstock
|
||||
// we try to statistical orders count to recognition this situation
|
||||
updateStatePrice(state);
|
||||
var details = state.details;
|
||||
var ordersCount = 0;
|
||||
if (diff > 0) {
|
||||
var attr = 'Sell';
|
||||
if (UseMarketOrder) {
|
||||
attr = 'Buy';
|
||||
}
|
||||
// Sell adjustDiff, sort by price high to low
|
||||
details.sort(function(a, b) {
|
||||
return b.ticker[attr] - a.ticker[attr];
|
||||
});
|
||||
for (var i = 0; i < details.length && adjustDiff >= state.minStock; i++) {
|
||||
if (isPriceNormal(details[i].ticker[attr]) && (details[i].account.Stocks >= state.minStock)) {
|
||||
var orderAmount = adjustFloat(Math.min(AmountOnce, adjustDiff, details[i].account.Stocks));
|
||||
var orderPrice = details[i].realTicker[attr] - SlidePrice;
|
||||
if ((orderPrice * orderAmount) < details[i].exchange.GetMinPrice()) {
|
||||
continue;
|
||||
}
|
||||
ordersCount++;
|
||||
if (details[i].exchange.Sell(orderPrice, orderAmount, stripTicker(details[i].ticker))) {
|
||||
adjustDiff = adjustFloat(adjustDiff - orderAmount);
|
||||
}
|
||||
// only operate one platform
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var attr = 'Buy';
|
||||
if (UseMarketOrder) {
|
||||
attr = 'Sell';
|
||||
}
|
||||
// Buy adjustDiff, sort by sell-price low to high
|
||||
details.sort(function(a, b) {
|
||||
return a.ticker[attr] - b.ticker[attr];
|
||||
});
|
||||
for (var i = 0; i < details.length && adjustDiff >= state.minStock; i++) {
|
||||
if (isPriceNormal(details[i].ticker[attr])) {
|
||||
var canRealBuy = adjustFloat(details[i].account.Balance / (details[i].ticker[attr] + SlidePrice));
|
||||
var needRealBuy = Math.min(AmountOnce, adjustDiff, canRealBuy);
|
||||
var orderAmount = adjustFloat(needRealBuy * (1 + (details[i].fee.Buy / 100)));
|
||||
var orderPrice = details[i].realTicker[attr] + SlidePrice;
|
||||
if ((orderAmount < details[i].exchange.GetMinStock()) ||
|
||||
((orderPrice * orderAmount) < details[i].exchange.GetMinPrice())) {
|
||||
continue;
|
||||
}
|
||||
ordersCount++;
|
||||
if (details[i].exchange.Buy(orderPrice, orderAmount, stripTicker(details[i].ticker))) {
|
||||
adjustDiff = adjustFloat(adjustDiff - needRealBuy);
|
||||
}
|
||||
// only operate one platform
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
isBalance = (ordersCount == 0);
|
||||
}
|
||||
|
||||
if (isBalance) {
|
||||
var currentProfit = getProfit(initState, state, lastAvgPrice);
|
||||
LogProfit(currentProfit, "Spread: ", adjustFloat((currentProfit - lastProfit) / lastOpAmount), "Balance: ", adjustFloat(state.allBalance), "Stocks: ", adjustFloat(state.allStocks));
|
||||
|
||||
if (StopWhenLoss && currentProfit < 0 && Math.abs(currentProfit) > MaxLoss) {
|
||||
Log('交易亏损超过最大限度, 程序取消所有订单后退出.');
|
||||
cancelAllOrders();
|
||||
if (SMSAPI.length > 10 && SMSAPI.indexOf('http') == 0) {
|
||||
HttpQuery(SMSAPI);
|
||||
Log('已经短信通知');
|
||||
}
|
||||
throw '已停止';
|
||||
}
|
||||
lastProfit = currentProfit;
|
||||
}
|
||||
}
|
||||
|
||||
function onTick() {
|
||||
if (!isBalance) {
|
||||
balanceAccounts();
|
||||
return;
|
||||
}
|
||||
|
||||
var state = getExchangesState();
|
||||
// We also need details of price
|
||||
updateStatePrice(state);
|
||||
|
||||
var details = state.details;
|
||||
var maxPair = null;
|
||||
var minPair = null;
|
||||
for (var i = 0; i < details.length; i++) {
|
||||
var sellOrderPrice = details[i].account.Stocks * (details[i].realTicker.Buy - SlidePrice);
|
||||
if (((!maxPair) || (details[i].ticker.Buy > maxPair.ticker.Buy)) && (details[i].account.Stocks >= state.minStock) &&
|
||||
(sellOrderPrice > details[i].exchange.GetMinPrice())) {
|
||||
details[i].canSell = details[i].account.Stocks;
|
||||
maxPair = details[i];
|
||||
}
|
||||
|
||||
var canBuy = adjustFloat(details[i].account.Balance / (details[i].realTicker.Sell + SlidePrice));
|
||||
var buyOrderPrice = canBuy * (details[i].realTicker.Sell + SlidePrice);
|
||||
if (((!minPair) || (details[i].ticker.Sell < minPair.ticker.Sell)) && (canBuy >= state.minStock) &&
|
||||
(buyOrderPrice > details[i].exchange.GetMinPrice())) {
|
||||
details[i].canBuy = canBuy;
|
||||
// how much coins we real got with fee
|
||||
details[i].realBuy = adjustFloat(details[i].account.Balance / (details[i].ticker.Sell + SlidePrice));
|
||||
minPair = details[i];
|
||||
}
|
||||
}
|
||||
|
||||
if ((!maxPair) || (!minPair) || ((maxPair.ticker.Buy - minPair.ticker.Sell) < MaxDiff) ||
|
||||
!isPriceNormal(maxPair.ticker.Buy) || !isPriceNormal(minPair.ticker.Sell)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// filter invalid price
|
||||
if (minPair.realTicker.Sell <= minPair.realTicker.Buy || maxPair.realTicker.Sell <= maxPair.realTicker.Buy) {
|
||||
return;
|
||||
}
|
||||
|
||||
// what a fuck...
|
||||
if (maxPair.exchange.GetName() == minPair.exchange.GetName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastAvgPrice = adjustFloat((minPair.realTicker.Buy + maxPair.realTicker.Buy) / 2);
|
||||
lastSpread = adjustFloat((maxPair.realTicker.Sell - minPair.realTicker.Buy) / 2);
|
||||
|
||||
// compute amount
|
||||
var amount = Math.min(AmountOnce, maxPair.canSell, minPair.realBuy);
|
||||
lastOpAmount = amount;
|
||||
var hedgePrice = adjustFloat((maxPair.realTicker.Buy - minPair.realTicker.Sell) / Math.max(SlideRatio, 2))
|
||||
if (minPair.exchange.Buy(minPair.realTicker.Sell + hedgePrice, amount * (1 + (minPair.fee.Buy / 100)), stripTicker(minPair.realTicker))) {
|
||||
maxPair.exchange.Sell(maxPair.realTicker.Buy - hedgePrice, amount, stripTicker(maxPair.realTicker));
|
||||
}
|
||||
|
||||
isBalance = false;
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (exchanges.length < 2) {
|
||||
throw "交易所数量最少得两个才能完成对冲";
|
||||
}
|
||||
|
||||
TickInterval = Math.max(TickInterval, 50);
|
||||
Interval = Math.max(Interval, 50);
|
||||
|
||||
cancelAllOrders();
|
||||
|
||||
initState = getExchangesState();
|
||||
if (initState.allStocks == 0) {
|
||||
throw "所有交易所货币数量总和为空, 必须先在任一交易所建仓才可以完成对冲";
|
||||
}
|
||||
if (initState.allBalance == 0) {
|
||||
throw "所有交易所CNY数量总和为空, 无法继续对冲";
|
||||
}
|
||||
|
||||
for (var i = 0; i < initState.details.length; i++) {
|
||||
var e = initState.details[i];
|
||||
Log(e.exchange.GetName(), e.exchange.GetCurrency(), e.account);
|
||||
}
|
||||
|
||||
Log("ALL: Balance: ", initState.allBalance, "Stocks: ", initState.allStocks, "Ver:", Version());
|
||||
|
||||
|
||||
while (true) {
|
||||
onTick();
|
||||
Sleep(parseInt(TickInterval));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/194150
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-15 14:13:49
|
||||
@@ -0,0 +1,398 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
币安永续多币种对冲策略原版(做多超跌做空超涨)4月13日最新止损模块
|
||||
|
||||
> 策略作者
|
||||
|
||||
小草
|
||||
|
||||
> 策略描述
|
||||
|
||||
## 4.16日更新内容
|
||||
|
||||
更改了止损的bug
|
||||
|
||||
修改了默认参数:
|
||||
|
||||
```
|
||||
var Alpha = 0.001 //指数移动平均的Alpha参数,设置的越大,基准价格跟踪越敏感,最终持仓也会越低,降低了杠杆,但会降低收益,具体需要根据回测结果自己权衡
|
||||
var Update_base_price_time_interval = 60 //多久更新一次基准价格, 单位秒,和Alpha参数相关,Alpha 设置的越小,这个间隔也可以设置的更小
|
||||
```
|
||||
|
||||
## 4.13 更新内容
|
||||
|
||||
Stop_loss设置为0.8表示当资金达到低于初始资金的80%时,止损,清空所有仓位,停止策略。随着策略运行,Stop_loss可以设置大于1(重启生效),比如从1000赚到1500,Stop_loss设置为1.3,则回撤到1300元止损。不想止损可以把这个参数设置的很小。风险是大家都用这种止损会形成踩踏,加大亏损。初始资金在状态栏的init_balance字段,注意提现等操作会影响,别不小心止损了。如果还是怕黑天鹅事件,比如某个币归0等,可以手动提现出来。
|
||||
|
||||
Max_diff和Min_diff限制了偏离的程度,需要根据自己的trade_value,总资金和风险承受能力自己确定。
|
||||
|
||||
举个简单例子,假如一共交易20个币,其中一个币价值逐渐上涨到偏离0.4不再交易,其它币价格一直不变会,亏损trade_value的7倍。不断下跌到偏离到偏离-0.3,将亏损trade_value的6倍。
|
||||
|
||||
```
|
||||
var Stop_loss = 0.8
|
||||
var Max_diff = 0.4 //当偏差diff大于0.4时,不继续加空仓, 自行设置
|
||||
var Min_diff = -0.3 //当diff小于-0.3时,不继续加多仓, 自行设置
|
||||
```
|
||||
|
||||
## 4.10 更新内容
|
||||
|
||||
**把策略代码复制到本地策略,直接覆盖保存即可,重启机器人生效,还会保持原有的仓位**
|
||||
|
||||
币安期货做空超涨做多超跌策略重要优化notebook代码地址:https://www.fmz.com/bbs-topic/5364
|
||||
|
||||
原有策略山寨币指数 = mean(sum((山寨币价格/比特币价格)/(山寨币初始价格/比特币初始价格)))。最大的问题是最新价格和策略启动的初始价格对比,随着时间的增长,会越来越偏离,一个币可能持有很多的仓位,风险很高,最后将持有很多仓位,增加了风险和回撤。
|
||||
|
||||
最新的山寨币指数 = mean(sum((山寨币价格/比特币价格)/EMA(山寨币价格/比特币价格))),即是和均线的价格进行对比,能够跟踪最新的价格变化,更加灵活,回测发现降低了策略持仓,也减少了回撤。更加稳定。最重要的是原策略如果加入几个异常交易对,风险极高,很可能爆仓,现在却几乎不受影响。
|
||||
|
||||
为了无缝升级,其中两个参数写到了策略代码的前两行,按需更改。
|
||||
|
||||
Alpha = 0.04 指数移动平局的Alpha参数,设置的越大,基准价格跟踪越敏感,交易的越少,最终持仓也会越低,降低了杠杆,但会降低收益,降低最大回撤,可以加大成交量,具体需要根据回测结果自己权衡。
|
||||
Update_base_price_time_interval = 30*60 多久更新一次基准价格, 单位秒,和Alpha参数相关,Alpha 设置的越小,这个间隔也可以设置的更小
|
||||
|
||||
如果你看了文章,想要交易全币种,这里是列表``ETH,BCH,XRP,EOS,LTC,TRX,ETC,LINK,XLM,ADA,XMR,DASH,ZEC,XTZ,BNB,ATOM,ONT,IOTA,BAT,VET,NEO,QTUM,IOST``
|
||||
|
||||
## **重要内容!!**
|
||||
|
||||
- 一定要先看这篇研究 https://www.fmz.com/digest-topic/5294 。了解策略原理、风险、交易对如何筛选、如何设置参数、开仓和总资金的比例等等一系列问题。
|
||||
- 上一篇研究报告需要下载并上传到自己的研究环境中。实际修改运行一遍。如果你已经看过这篇报告,最近又更新了最新一周的数据。
|
||||
- **策略不能直接回测,需要在研究环境中回测**。
|
||||
- 策略代码以及默认参数仅供研究,实盘运行需要谨慎,根据自己的研究决定参数,**自担风险**。
|
||||
- **策略不可能每天盈利,可看回测历史,1-2周的横盘和回撤都是正常的,需要正确对待**
|
||||
- 代码是公开的,可以自行修改,如果有任何问题,欢迎评论反馈,最好加入发明者币安交流群(研究报告中有加入方法),可获得更新通知
|
||||
- **策略仅支持币安期货,需要在全仓模式运行,不要设置双向持仓!!,创建机器人时用默认交易对和K线周期即可,策略没有用到K线**
|
||||
- **策略和其他策略以及手动操作是冲突的,需要注意**
|
||||
- 实盘运行需要海外托管者,测试阶段可在平台一键租用阿里云香港服务器,自己按月整租更便宜(配置最低的即可,部署教程:https://www.fmz.com/bbs-topic/2848)
|
||||
- 币安的期货和现货需要单独添加,币安期货为``Futures_Binance``
|
||||
- 此策略重启不影响,但是新建机器人会重新记录历史数据。
|
||||
- 策略根据用户反馈可能会有更新,直接ctrl+A复制代码覆盖保存(一般不会更新参数),重启机器人即可用上最新代码。
|
||||
- 策略一开始并不会交易,第一次启动需要记录数据,需要等待行情的变化才会交易
|
||||
|
||||
## 加入微信群参加币安千团大战获取更新
|
||||
|
||||
加下方微信号,回复“币安”即自动拉入群:
|
||||
|
||||
https://www.fmz.com
|
||||
|
||||
## 策略原理
|
||||
|
||||
将做空价格高于山寨币-比特币价格指数的币种,做多低于指数的币种,偏离越大,仓位越大。(本策略没有把不对等的仓位用BTC对冲,也可以把BTC加入交易对)。最近两个月的表现(3倍左右杠杆,数据更新到4.8):
|
||||

|
||||
|
||||
## 策略逻辑
|
||||
|
||||
1.更新行情和账户持仓,第一次运行会记录初始价格(新加入的币种按照加入时间计算)
|
||||
2.更新指数,指数是山寨币-比特币价格指数 = mean(sum((山寨币价格/比特币价格)/(山寨币初始价格/比特币初始价格)))
|
||||
3.根据偏离指数判断做多做空,根据偏离大小判断仓位
|
||||
4.下单,下单量由冰山委托决定,按照对手价成交(买入用卖一价)。**下单后立即撤销(所以会看到很多撤销失败的单子400: {"code":-2011,"msg":"Unknown order sent."},正常现象)**
|
||||
5.再次循环
|
||||
|
||||
状态栏中的leverage代表保证金已用占比,需要保持较低水平以满足新的开仓
|
||||
|
||||
## 策略参数
|
||||
|
||||

|
||||
|
||||
- Trade_symbols:交易的币种,需要自己根据研究平台自己筛选,也可以加入BTC
|
||||
- Trade_value:山寨币价格(BTC计价)每偏离指数1%持有价值,需要根据自己投入的总资金和风险偏好决定,建议设置为总资金的3-10%。可通过研究环境的回测看杠杆的大小,Trade_value可以小于Adjust_value,如Adjust_value的一半,这样相当于偏离指数2%的持有价值。
|
||||
- Adjust_value: 合约价值(USDT计价)调整偏离值。 当 指数偏离 \* Trade_value - 当前持仓 > Adjust_value,即目标持仓和当前持仓的差超过此值,就会开始交易。过大调整较慢,太小交易频繁,不能低于10,否则会达不到最小成交,推荐设置为Trade_value的40%以上。
|
||||
- Ice_value:冰山委托价值,同样不能低于10,实际下单选Adjust_value与Ice_value 中较小的那一个,如果资金较多可以设置的相对大一些这样调整的更快,建议不低于Adjust_value的20%,这样5次冰山就能完成成交,当然,Trade_value不大时,Ice_value可以设置的相对大一下,一两次就能调整到位。
|
||||
- Interval:循环休眠时间,可以设置的小一些,如1s,但不能超过币安频率限制。
|
||||
- Reset:重置历史数据,会将策略参考的初始价格重新设置为当前的价格,一般不用设置
|
||||
|
||||
|
||||
## 策略风险
|
||||
|
||||
注意到如果某个币走出了独立的行情,比如相对于指数上涨了几倍,将会在该币种上积累大量的做空仓位,同样的大幅下跌也会使得策略大量做多。可限制开仓量或者止损不再交易。
|
||||
|
||||
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Trade_symbols|IOST,TRX,XLM,QTUM,DASH,ADA,BNB,XMR,ZEC,ATOM,IOTA,NEO,ONT,XRP,BAT,VET,EOS,ETC,LTC|交易的币种|
|
||||
|Trade_value|20|每偏离指数1%持有价值|
|
||||
|Adjust_value|20|合约价值调整偏离值|
|
||||
|Ice_value|20|冰山委托订单的大小|
|
||||
|Log_profit_interval|600|Log总权益间隔s|
|
||||
|Interval|true|休眠时间s|
|
||||
|Reset|false|重置历史数据|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
|
||||
|
||||
var Alpha = 0.001 //指数移动平均的Alpha参数,设置的越大,基准价格跟踪越敏感,最终持仓也会越低,降低了杠杆,但会降低收益,具体需要根据回测结果自己权衡
|
||||
var Update_base_price_time_interval = 60 //多久更新一次基准价格, 单位秒,和Alpha参数相关,Alpha 设置的越小,这个间隔也可以设置的更小
|
||||
|
||||
//Stop_loss设置为0.8表示当资金达到低于初始资金的80%时,止损,清空所有仓位,停止策略。
|
||||
//随着策略运行,Stop_loss可以设置大于1(重启生效),比如从1000赚到1500,Stop_loss设置为1.3,则回撤到1300元止损。不想止损可以把这个参数设置的很小。
|
||||
//风险是大家都用这种止损会形成踩踏,加大亏损。
|
||||
//初始资金在状态栏的init_balance字段,注意提现等操作会影响,别不小心止损了。
|
||||
//如果还是怕黑天鹅事件,比如某个币归0等,可以手动提现出来。
|
||||
|
||||
var Stop_loss = 0.8
|
||||
var Max_diff = 0.4 //当偏差diff大于0.4时,不继续加空仓, 自行设置
|
||||
var Min_diff = -0.3 //当diff小于-0.3时,不继续加多仓, 自行设置
|
||||
|
||||
if(IsVirtual()){
|
||||
throw '不能回测,回测参考 https://www.fmz.com/digest-topic/5294 '
|
||||
}
|
||||
if(exchange.GetName() != 'Futures_Binance'){
|
||||
throw '只支持币安期货交易所,和现货交易所不同,需要单独添加,名称为Futures_Binance'
|
||||
}
|
||||
var trade_symbols = Trade_symbols.split(',')
|
||||
var symbols = trade_symbols
|
||||
var index = 1 //指数
|
||||
if(trade_symbols.indexOf('BTC')<0){
|
||||
symbols = trade_symbols.concat(['BTC'])
|
||||
}
|
||||
var update_profit_time = 0
|
||||
var update_base_price_time= Date.now()
|
||||
var assets = {}
|
||||
var init_prices = {}
|
||||
|
||||
|
||||
var trade_info = {}
|
||||
var exchange_info = HttpQuery('https://fapi.binance.com/fapi/v1/exchangeInfo')
|
||||
if(!exchange_info){
|
||||
throw '无法连接币安网络,需要海外托管者'
|
||||
}
|
||||
exchange_info = JSON.parse(exchange_info)
|
||||
for (var i=0; i<exchange_info.symbols.length; i++){
|
||||
if(symbols.indexOf(exchange_info.symbols[i].baseAsset) > -1){
|
||||
assets[exchange_info.symbols[i].baseAsset] = {amount:0, hold_price:0, value:0, bid_price:0, ask_price:0,
|
||||
btc_price:0, btc_change:1,btc_diff:0,
|
||||
realised_profit:0, margin:0, unrealised_profit:0}
|
||||
trade_info[exchange_info.symbols[i].baseAsset] = {minQty:parseFloat(exchange_info.symbols[i].filters[1].minQty),
|
||||
priceSize:parseInt((Math.log10(1.1/parseFloat(exchange_info.symbols[i].filters[0].tickSize)))),
|
||||
amountSize:parseInt((Math.log10(1.1/parseFloat(exchange_info.symbols[i].filters[1].stepSize))))
|
||||
}
|
||||
}
|
||||
}
|
||||
assets.USDT = {unrealised_profit:0, margin:0, margin_balance:0, total_balance:0, leverage:0, update_time:0, init_balance:0, stop_balance:0, profit:0}
|
||||
|
||||
function updateAccount(){ //更新账户和持仓
|
||||
var account = exchange.GetAccount()
|
||||
var pos = exchange.GetPosition()
|
||||
if (account == null || pos == null ){
|
||||
Log('update account time out')
|
||||
return
|
||||
}
|
||||
assets.USDT.update_time = Date.now()
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
assets[trade_symbols[i]].margin = 0
|
||||
assets[trade_symbols[i]].unrealised_profit = 0
|
||||
assets[trade_symbols[i]].hold_price = 0
|
||||
assets[trade_symbols[i]].amount = 0
|
||||
}
|
||||
for(var j=0; j<account.Info.positions.length; j++){
|
||||
if(account.Info.positions[j].positionSide == 'BOTH'){
|
||||
var pair = account.Info.positions[j].symbol
|
||||
var coin = pair.slice(0,pair.length-4)
|
||||
if(symbols.indexOf(coin) < 0){continue}
|
||||
assets[coin].margin = parseFloat(account.Info.positions[j].initialMargin) + parseFloat(account.Info.positions[j].maintMargin)
|
||||
assets[coin].unrealised_profit = parseFloat(account.Info.positions[j].unrealizedProfit)
|
||||
}
|
||||
}
|
||||
assets.USDT.margin = _N(parseFloat(account.Info.totalInitialMargin) + parseFloat(account.Info.totalMaintMargin),2)
|
||||
assets.USDT.margin_balance = _N(parseFloat(account.Info.totalMarginBalance),2)
|
||||
assets.USDT.total_balance = _N(parseFloat(account.Info.totalWalletBalance),2)
|
||||
if(assets.USDT.init_balance == 0){
|
||||
if(_G('init_balance')){
|
||||
assets.USDT.init_balance = _N(_G('init_balance'),2)
|
||||
}else{
|
||||
assets.USDT.init_balance = assets.USDT.total_balance
|
||||
_G('init_balance',assets.USDT.init_balance)
|
||||
}
|
||||
}
|
||||
assets.USDT.stop_balance = _N(Stop_loss*assets.USDT.init_balance, 2)
|
||||
assets.USDT.total_balance = _N(parseFloat(account.Info.totalWalletBalance),2)
|
||||
assets.USDT.unrealised_profit = _N(parseFloat(account.Info.totalUnrealizedProfit),2)
|
||||
assets.USDT.leverage = _N(assets.USDT.margin/assets.USDT.total_balance,2)
|
||||
pos = JSON.parse(exchange.GetRawJSON())
|
||||
if(pos.length > 0){
|
||||
for(var k=0; k<pos.length; k++){
|
||||
var pair = pos[k].symbol
|
||||
var coin = pair.slice(0,pair.length-4)
|
||||
if(symbols.indexOf(coin) < 0){continue}
|
||||
if(pos[k].positionSide != 'BOTH'){continue}
|
||||
assets[coin].hold_price = parseFloat(pos[k].entryPrice)
|
||||
assets[coin].amount = parseFloat(pos[k].positionAmt)
|
||||
assets[coin].unrealised_profit = parseFloat(pos[k].unRealizedProfit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateIndex(){ //更新指数
|
||||
|
||||
if(!_G('init_prices') || Reset){
|
||||
Reset = false
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
init_prices[trade_symbols[i]] = (assets[trade_symbols[i]].ask_price+assets[trade_symbols[i]].bid_price)/(assets.BTC.ask_price+assets.BTC.bid_price)
|
||||
}
|
||||
Log('保存启动时的价格')
|
||||
_G('init_prices',init_prices)
|
||||
}else{
|
||||
init_prices = _G('init_prices')
|
||||
if(Date.now() - update_base_price_time > Update_base_price_time_interval*1000){
|
||||
update_base_price_time = Date.now()
|
||||
for(var i=0; i<trade_symbols.length; i++){ //更新初始价格
|
||||
init_prices[trade_symbols[i]] = init_prices[trade_symbols[i]]*(1-Alpha)+Alpha*(assets[trade_symbols[i]].ask_price+assets[trade_symbols[i]].bid_price)/(assets.BTC.ask_price+assets.BTC.bid_price)
|
||||
}
|
||||
_G('init_prices',init_prices)
|
||||
//Log(init_prices)
|
||||
//Log('更新了一次基准价格')
|
||||
}
|
||||
var temp = 0
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
assets[trade_symbols[i]].btc_price = (assets[trade_symbols[i]].ask_price+assets[trade_symbols[i]].bid_price)/(assets.BTC.ask_price+assets.BTC.bid_price)
|
||||
if(!(trade_symbols[i] in init_prices)){
|
||||
Log('添加新的币种',trade_symbols[i])
|
||||
init_prices[trade_symbols[i]] = assets[trade_symbols[i]].btc_price
|
||||
_G('init_prices',init_prices)
|
||||
}
|
||||
assets[trade_symbols[i]].btc_change = _N(assets[trade_symbols[i]].btc_price/init_prices[trade_symbols[i]],4)
|
||||
temp += assets[trade_symbols[i]].btc_change
|
||||
}
|
||||
index = _N(temp/trade_symbols.length, 4)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function updateTick(){ //更新行情
|
||||
var ticker = HttpQuery('https://fapi.binance.com/fapi/v1/ticker/bookTicker')
|
||||
try {
|
||||
ticker = JSON.parse(ticker)
|
||||
}catch(e){
|
||||
Log('get ticker time out')
|
||||
return
|
||||
}
|
||||
for(var i=0; i<ticker.length; i++){
|
||||
var pair = ticker[i].symbol
|
||||
var coin = pair.slice(0,pair.length-4)
|
||||
if(symbols.indexOf(coin) < 0){continue}
|
||||
assets[coin].ask_price = parseFloat(ticker[i].askPrice)
|
||||
assets[coin].bid_price = parseFloat(ticker[i].bidPrice)
|
||||
assets[coin].ask_value = _N(assets[coin].amount*assets[coin].ask_price, 2)
|
||||
assets[coin].bid_value = _N(assets[coin].amount*assets[coin].bid_price, 2)
|
||||
}
|
||||
updateIndex()
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
assets[trade_symbols[i]].btc_diff = _N(assets[trade_symbols[i]].btc_change - index, 4)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function trade(symbol, dirction, value){ //交易
|
||||
if(Date.now()-assets.USDT.update_time > 10*1000){
|
||||
Log('更新账户延时,不交易')
|
||||
return
|
||||
}
|
||||
var price = dirction == 'sell' ? assets[symbol].bid_price : assets[symbol].ask_price
|
||||
var amount = _N(Math.min(value,Ice_value)/price, trade_info[symbol].amountSize)
|
||||
if(amount < trade_info[symbol].minQty){
|
||||
Log(symbol, '合约价值偏离或冰山委托订单的大小设置过小,达不到最小成交, 至少需要: ', _N(trade_info[symbol].minQty*price,0)+1)
|
||||
return
|
||||
}
|
||||
exchange.IO("currency", symbol+'_'+'USDT')
|
||||
exchange.SetContractType('swap')
|
||||
exchange.SetDirection(dirction)
|
||||
var f = dirction == 'buy' ? 'Buy' : 'Sell'
|
||||
var id = exchange[f](price, amount, symbol)
|
||||
if(id){
|
||||
exchange.CancelOrder(id) //订单会立即撤销
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
|
||||
|
||||
function updateStatus(){ //状态栏信息
|
||||
var table = {type: 'table', title: '交易对信息',
|
||||
cols: ['币种', '数量', '持仓价格', '当前价格', '偏离平均', '持仓价值', '保证金', '未实现盈亏'],
|
||||
rows: []}
|
||||
for (var i=0; i<symbols.length; i++){
|
||||
var price = _N((assets[symbols[i]].ask_price + assets[symbols[i]].bid_price)/2, trade_info[symbols[i]].priceSize)
|
||||
var value = _N((assets[symbols[i]].ask_value + assets[symbols[i]].bid_value)/2, 2)
|
||||
var infoList = [symbols[i], assets[symbols[i]].amount, assets[symbols[i]].hold_price, price, assets[symbols[i]].btc_diff, value, _N(assets[symbols[i]].margin,3), _N(assets[symbols[i]].unrealised_profit,3)]
|
||||
table.rows.push(infoList)
|
||||
}
|
||||
var logString = _D() + ' ' + JSON.stringify(assets.USDT) + ' Index:' + index + '\n'
|
||||
LogStatus(logString + '`' + JSON.stringify(table) + '`')
|
||||
|
||||
if(Date.now()-update_profit_time > Log_profit_interval*1000){
|
||||
LogProfit(_N(assets.USDT.margin_balance,3))
|
||||
update_profit_time = Date.now()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function stopLoss(){ //止损函数
|
||||
while(true){
|
||||
if(assets.USDT.margin_balance < Stop_loss*assets.USDT.init_balance && assets.USDT.init_balance > 0){
|
||||
Log('触发止损,当前资金:', assets.USDT.margin_balance, '初始资金:', assets.USDT.init_balance)
|
||||
Ice_value = 200 //止损的快一些,可修改
|
||||
updateAccount()
|
||||
updateTick()
|
||||
var trading = false //是否正在交易
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
var symbol = trade_symbols[i]
|
||||
if(assets[symbol].ask_price == 0){ continue }
|
||||
if(assets[symbol].bid_value >= trade_info[symbol].minQty*assets[symbol].bid_price){
|
||||
trade(symbol, 'sell', assets[symbol].bid_value)
|
||||
trading = true
|
||||
}
|
||||
if(assets[symbol].ask_value <= -trade_info[symbol].minQty*assets[symbol].ask_price){
|
||||
trade(symbol, 'buy', -assets[symbol].ask_value)
|
||||
trading = true
|
||||
}
|
||||
}
|
||||
Sleep(1000)
|
||||
if(!trading){
|
||||
throw '止损结束,如果需要重新运行策略,需要调低止损'
|
||||
}
|
||||
}else{ //不用止损
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onTick(){ //策略逻辑部分
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
var symbol = trade_symbols[i]
|
||||
if(assets[symbol].ask_price == 0){ continue }
|
||||
var aim_value = -Trade_value * _N(assets[symbol].btc_diff/0.01,3)
|
||||
if(aim_value - assets[symbol].ask_value >= Adjust_value && assets[symbol].btc_diff > Min_diff){
|
||||
trade(symbol,'buy', aim_value - assets[symbol].ask_value)
|
||||
}
|
||||
if(aim_value - assets[symbol].bid_value <= -Adjust_value && assets[symbol].btc_diff < Max_diff){
|
||||
trade(symbol,'sell', -(aim_value - assets[symbol].bid_value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
while(true){
|
||||
updateAccount()
|
||||
updateTick()
|
||||
stopLoss() //止损
|
||||
onTick()
|
||||
updateStatus()
|
||||
Sleep(Interval*1000)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/195226
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-16 17:33:37
|
||||
@@ -0,0 +1,266 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
币安永续多币种对冲策略(做空或做多山寨币指数)4月10日改进Bug,需要更新
|
||||
|
||||
> 策略作者
|
||||
|
||||
小草
|
||||
|
||||
> 策略描述
|
||||
|
||||
## **重要内容!!**
|
||||
|
||||
- 一定要先看这篇研究 https://www.fmz.com/digest-topic/5294 。了解策略原理、风险、交易对如何筛选、如何设置参数、开仓和总资金的比例等等一系列问题。
|
||||
- 上一篇研究报告需要下载并上传到自己的研究环境中。实际修改运行一遍。如果你已经看过这篇报告,最近又更新了最新一周的数据。
|
||||
- **策略不能直接回测,需要在研究环境中回测**。
|
||||
- 策略代码以及默认参数仅供研究,实盘运行需要谨慎,根据自己的研究决定参数,**自担风险**。
|
||||
- **策略不可能每天盈利,可看回测历史,1-2周的横盘和回撤都是正常的,并且有可能回测很大,需要正确对待**
|
||||
- 代码是公开的,可以自行修改,如果有任何问题,欢迎评论反馈,最好加入发明者币安交流群(研究报告中有加入方法),可获得更新通知
|
||||
- **策略需要在全仓模式运行,不要设置双向持仓,策略只支持币安期货,创建机器人时用默认交易对和K线周期即可,策略没有用到K线**
|
||||
- **策略和其他策略以及手动操作是冲突的,需要注意**
|
||||
- 实盘运行需要海外托管者,测试阶段可在平台一键租用阿里云香港服务器,自己按月整租更便宜(配置最低的即可,部署教程: https://www.fmz.com/bbs-topic/2848 )
|
||||
- 币安的期货和现货需要单独添加,币安期货为``Futures_Binance``
|
||||
|
||||
## 策略原理
|
||||
|
||||
策略将分散等值做空选定的一篮子山寨币,同时等仓位做多比特币对冲,降低风险和波动率。随着价格的波动,不断调整仓位保持空头价值恒定和多头仓位对等。**本质上时做空山寨币-比特币价格指数**。最近两个月的表现(3倍左右杠杆,数据更新到4.8),最近一周山寨币相对于比特币是上涨的,因此亏损,如果你看多山寨币,可以在参数中设置做空比特币做多山寨币:
|
||||
|
||||
**策略默认是做多比特币做空山寨币,你也可以反过来(假如你认为山寨币处于底部),决定权在自己**
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## 策略逻辑
|
||||
|
||||
1.更新行情和账户持仓
|
||||
2.更新各个山寨币空头持仓价值,判断空头是否需要调整持仓
|
||||
3.更新总空头的仓位,确定多头持仓,判断多头仓位是否要调整
|
||||
4.下单,下单量由冰山委托决定,按照对手价成交(买入用卖一价)。**下单后立即撤销(所以会看到很多撤销失败的单子400: {"code":-2011,"msg":"Unknown order sent."},正常现象)**
|
||||
5.再次循环
|
||||
|
||||
**会判断Short_symbols,Long_symbols那个交易对多,多的每个币种开仓价值为Trade_value,少的每个币种合约价值为需要对冲价值的平均。**
|
||||
|
||||
如果只空BTC,多TRX,DASH,ONT,QTUM,Trade_value为50,则TRX,DASH,ONT,QTUM均有多仓50,BTC持有空仓50\*4。
|
||||
|
||||
如果只多BTC,空TRX,DASH,ONT,QTUM,Trade_value为50,则TRX,DASH,ONT,QTUM均有空仓50,BTC持有多仓50\*4。
|
||||
|
||||
状态栏中的leverage代表保证金已用占比,不宜过高。
|
||||
|
||||
## 策略参数
|
||||
|
||||

|
||||
|
||||
- Short_symbols:做空的币种,用“ , ”隔开
|
||||
- Long_symbols:做多的币种,也可以留空,不对冲,直接裸空。
|
||||
- Trade_value:单个币种做空持有价值。还要做多对冲,总价值=2\*Trade_value\*做空币种数。一般使用3-5倍杠杆,即总价值=3*账户余额。需要根据自己投入的总资金决定,可通过研究环境的回测看杠杆的大小
|
||||
- Adjust_value: 合约价值(USDT计价)调整偏离值,过大调整较慢,太小手续费过高,根据Trade_value自行决定。不能低于20,否则会达不到最小成交
|
||||
- Ice_value:冰山委托价值,同样不能低于20,实际下单选Adjust_value与Ice_value 中较小的那一个
|
||||
|
||||
|
||||
## 策略风险
|
||||
|
||||
当做空的币价上涨,合约价值增加,此时是减仓的,反之盈利是加仓。这使得总的合约价值维持恒定。山寨币是很有可能走出独立的行情的,目前从一年的周期看,山寨币可能处于底部,并且有可能从底部抬升不少。这取决与如何使用,如果你看好山寨币并认为已经到底部,可以方向操作,做多指数。或者你看好某几个币种(不一定是比特币),可以和它们对冲。
|
||||
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Short_symbols|TRX,DASH,ONT,QTUM,BAT,IOST,ADA,ZEC,XMR,NEO,VET,XRP,IOTA,XLM|做空的交易对|
|
||||
|Long_symbols|BTC|做多的交易对|
|
||||
|Trade_value|50|做空的单个合约价值|
|
||||
|Adjust_value|20|合约价值调整偏离值|
|
||||
|Ice_value|50|冰山委托订单的大小|
|
||||
|Log_profit_interval|20|Log总权益间隔s|
|
||||
|Interval|5|休眠时间s|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
if(IsVirtual()){
|
||||
throw '不能回测,回测参考 https://www.fmz.com/digest-topic/5294 '
|
||||
}
|
||||
if(exchange.GetName() != 'Futures_Binance'){
|
||||
throw '只支持币安期货交易所,和现货交易所不同,需要单独添加,名称为Futures_Binance'
|
||||
}
|
||||
|
||||
var short_symbols = Short_symbols.split(',')
|
||||
var long_symbols = Long_symbols.split(',')
|
||||
|
||||
var symbols = short_symbols.concat(long_symbols)
|
||||
var update_profit_time = 0
|
||||
var assets = {}
|
||||
var trade_info = {}
|
||||
var exchange_info = HttpQuery('https://fapi.binance.com/fapi/v1/exchangeInfo')
|
||||
if(!exchange_info){
|
||||
throw '无法连接币安网络,需要海外托管者'
|
||||
}
|
||||
exchange_info = JSON.parse(exchange_info)
|
||||
for (var i=0; i<exchange_info.symbols.length; i++){
|
||||
if(symbols.indexOf(exchange_info.symbols[i].baseAsset) > -1){
|
||||
assets[exchange_info.symbols[i].baseAsset] = {amount:0, hold_price:0, value:0, bid_price:0, ask_price:0, realised_profit:0, margin:0, unrealised_profit:0}
|
||||
trade_info[exchange_info.symbols[i].baseAsset] = {minQty:parseFloat(exchange_info.symbols[i].filters[1].minQty),
|
||||
priceSize:parseInt((Math.log10(1.1/parseFloat(exchange_info.symbols[i].filters[0].tickSize)))),
|
||||
amountSize:parseInt((Math.log10(1.1/parseFloat(exchange_info.symbols[i].filters[1].stepSize))))
|
||||
}
|
||||
}
|
||||
}
|
||||
assets.USDT = {unrealised_profit:0, margin:0, margin_balance:0, total_balance:0, leverage:0}
|
||||
|
||||
|
||||
function updateAccount(){
|
||||
var account = exchange.GetAccount()
|
||||
var pos = exchange.GetPosition()
|
||||
if (account == null || pos == null ){
|
||||
Log('update account time out')
|
||||
return
|
||||
}
|
||||
assets.USDT.update_time = Date.now()
|
||||
for(var i=0; i<symbols.length; i++){
|
||||
assets[symbols[i]].margin = 0
|
||||
assets[symbols[i]].unrealised_profit = 0
|
||||
assets[symbols[i]].hold_price = 0
|
||||
assets[symbols[i]].amount = 0
|
||||
assets[symbols[i]].unrealised_profit = 0
|
||||
}
|
||||
for(var j=0; j<account.Info.positions.length; j++){
|
||||
var pair = account.Info.positions[j].symbol
|
||||
var coin = pair.slice(0,pair.length-4)
|
||||
if(symbols.indexOf(coin) < 0){continue}
|
||||
assets[coin].margin = parseFloat(account.Info.positions[j].initialMargin) + parseFloat(account.Info.positions[j].maintMargin)
|
||||
assets[coin].unrealised_profit = parseFloat(account.Info.positions[j].unrealizedProfit)
|
||||
}
|
||||
assets.USDT.margin = _N(parseFloat(account.Info.totalInitialMargin) + parseFloat(account.Info.totalMaintMargin),2)
|
||||
assets.USDT.margin_balance = _N(parseFloat(account.Info.totalMarginBalance),2)
|
||||
assets.USDT.total_balance = _N(parseFloat(account.Info.totalWalletBalance),2)
|
||||
assets.USDT.unrealised_profit = _N(parseFloat(account.Info.totalUnrealizedProfit),2)
|
||||
assets.USDT.leverage = _N(assets.USDT.margin/assets.USDT.total_balance,2)
|
||||
pos = JSON.parse(exchange.GetRawJSON())
|
||||
if(pos.length > 0){
|
||||
for(var k=0; k<pos.length; k++){
|
||||
var pair = pos[k].symbol
|
||||
var coin = pair.slice(0,pair.length-4)
|
||||
if(symbols.indexOf(coin) < 0){continue}
|
||||
assets[coin].hold_price = parseFloat(pos[k].entryPrice)
|
||||
assets[coin].amount = parseFloat(pos[k].positionAmt)
|
||||
assets[coin].unrealised_profit = parseFloat(pos[k].unRealizedProfit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateTick(){
|
||||
var ticker = HttpQuery('https://fapi.binance.com/fapi/v1/ticker/bookTicker')
|
||||
if(ticker == null){
|
||||
Log('get ticker time out')
|
||||
return
|
||||
}
|
||||
ticker = JSON.parse(ticker)
|
||||
for(var i=0; i<ticker.length; i++){
|
||||
var pair = ticker[i].symbol
|
||||
var coin = pair.slice(0,pair.length-4)
|
||||
if(symbols.indexOf(coin) < 0){continue}
|
||||
assets[coin].ask_price = parseFloat(ticker[i].askPrice)
|
||||
assets[coin].bid_price = parseFloat(ticker[i].bidPrice)
|
||||
assets[coin].ask_value = _N(assets[coin].amount*assets[coin].ask_price, 2)
|
||||
assets[coin].bid_value = _N(assets[coin].amount*assets[coin].bid_price, 2)
|
||||
}
|
||||
}
|
||||
|
||||
function trade(symbol, dirction, value){
|
||||
if(Date.now()-assets.USDT.update_time > 10*1000){
|
||||
Log('更新账户延时,不交易')
|
||||
return
|
||||
}
|
||||
var price = dirction == 'sell' ? assets[symbol].bid_price : assets[symbol].ask_price
|
||||
var amount = _N(Math.min(value,Ice_value)/price, trade_info[symbol].amountSize)
|
||||
if(amount < trade_info[symbol].minQty){
|
||||
Log(symbol, '合约调整偏离价值或冰山委托订单设置过小,达不到最小成交, 至少需要: ', _N(trade_info[symbol].minQty*price,0))
|
||||
return
|
||||
}
|
||||
exchange.IO("currency", symbol+'_'+'USDT')
|
||||
exchange.SetContractType('swap')
|
||||
exchange.SetDirection(dirction)
|
||||
var f = dirction == 'buy' ? 'Buy' : 'Sell'
|
||||
var id = exchange[f](price, amount, symbol)
|
||||
if(id){
|
||||
exchange.CancelOrder(id) //订单会立即撤销
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function updateStatus(){
|
||||
var table = {type: 'table', title: '交易对信息',
|
||||
cols: ['币种', '数量', '持仓价格', '当前价格', '持仓价值', '保证金', '未实现盈亏'],
|
||||
rows: []}
|
||||
for (var i=0; i<symbols.length; i++){
|
||||
var price = _N((assets[symbols[i]].ask_price + assets[symbols[i]].bid_price)/2, trade_info[symbols[i]].priceSize)
|
||||
var value = _N((assets[symbols[i]].ask_value + assets[symbols[i]].bid_value)/2, 2)
|
||||
var infoList = [symbols[i], assets[symbols[i]].amount, assets[symbols[i]].hold_price, price, value,_N(assets[symbols[i]].margin,3), _N(assets[symbols[i]].unrealised_profit,3)]
|
||||
table.rows.push(infoList)
|
||||
}
|
||||
var logString = _D() + ' ' + JSON.stringify(assets.USDT) + '\n'
|
||||
LogStatus(logString + '`' + JSON.stringify(table) + '`')
|
||||
|
||||
if(Date.now()-update_profit_time > Log_profit_interval*1000){
|
||||
LogProfit(_N(assets.USDT.margin_balance,3))
|
||||
update_profit_time = Date.now()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onTick(){
|
||||
var short_value = Trade_value
|
||||
if(short_symbols.length<long_symbols.length){
|
||||
short_value = _N(long_symbols.length*Trade_value/short_symbols.length,0)
|
||||
}
|
||||
var long_value = Trade_value
|
||||
if(short_symbols.length>long_symbols.length){
|
||||
long_value = _N(short_symbols.length*Trade_value/long_symbols.length,0)
|
||||
}
|
||||
var symbol = ''
|
||||
for(var i=0; i<short_symbols.length; i++){
|
||||
symbol = short_symbols[i]
|
||||
if(assets[symbol].ask_price == 0){ continue }
|
||||
if(assets[symbol].bid_value + short_value > Adjust_value){
|
||||
trade(symbol, 'sell', assets[symbol].bid_value + short_value)
|
||||
}
|
||||
if(assets[symbol].ask_value + short_value < -Adjust_value){
|
||||
trade(symbol, 'buy', -(assets[symbol].ask_value + short_value))
|
||||
}
|
||||
}
|
||||
for(var i=0; i<long_symbols.length; i++){
|
||||
symbol = long_symbols[i]
|
||||
if(assets[symbol].ask_price == 0){ continue }
|
||||
if(assets[symbol].bid_value - long_value > Adjust_value){
|
||||
trade(symbol, 'sell', assets[symbol].bid_value-long_value)
|
||||
}
|
||||
if(assets[symbol].ask_value - long_value < -Adjust_value){
|
||||
trade(symbol, 'buy', long_value-assets[symbol].ask_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
while(true){
|
||||
updateAccount()
|
||||
updateTick()
|
||||
onTick()
|
||||
updateStatus()
|
||||
Sleep(Interval*1000)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/194825
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-14 23:02:44
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
币安策略二去掉高低分
|
||||
|
||||
> 策略作者
|
||||
|
||||
tomjava
|
||||
|
||||
> 策略描述
|
||||
|
||||
感谢FMZ公开策略,感谢卫兵支持!
|
||||
本人做了两项修改:
|
||||
1、因为很多朋友默认都是10倍,20倍杠杆,而该策略为全仓模式,一个币爆仓,就会被团灭。所以加上了一个Max_amount,单个币种买入量不会超过这个数,防止爆仓
|
||||
2、如果币池里有超涨超跌,特例独行的币,容易把大家拖下水,整体策略容易失效。因此我计算指数时去掉一个最高分,去掉一个最低分,得出的指数更公平。
|
||||
等特例独行的币恢复正常,还是可以正常买入的。
|
||||
注意:因条件所限,本策略未经回测,仅供参考,如有损失,概不负责!
|
||||
|
||||
## **重要内容!!**
|
||||
|
||||
- 一定要先看这篇研究 https://www.fmz.com/digest-topic/5294 。了解策略原理、风险、交易对如何筛选、如何设置参数、开仓和总资金的比例等等一系列问题。
|
||||
- 上一篇研究报告需要下载并上传到自己的研究环境中。实际修改运行一遍。如果你已经看过这篇报告,最近又更新了最新一周的数据。
|
||||
- **策略不能直接回测,需要在研究环境中回测**。
|
||||
- 策略代码以及默认参数仅供研究,实盘运行需要谨慎,自担风险。
|
||||
- 代码是公开的,可以自行修改,如果有任何问题,欢迎评论反馈,最好加入发明者币安交流群(研究报告中有加入方法)
|
||||
- 策略需要在全仓模式运行,**策略支持币安,创建机器人时用默认交易对和K线周期即可,策略没有用到K线**
|
||||
|
||||
## 策略原理
|
||||
|
||||
将做空价格高于山寨币-比特币价格指数的币种,做多低于指数的币种,偏离越大,仓位越大。(本策略没有对冲,也可以把BTC加入交易对)。最近两个月的表现(3倍左右杠杆,数据更新到4.8):
|
||||

|
||||
|
||||
## 策略逻辑
|
||||
|
||||
1.更新行情和账户持仓,第一次运行会记录初始价格(新加入的币种按照加入时间计算)
|
||||
2.更新指数,指数是山寨币-比特币价格指数 = mean(sum((山寨币价格/比特币价格)/(山寨币初始价格/比特币初始价格)))
|
||||
3.根据偏离指数判断做多做空,根据偏离大小判断仓位
|
||||
4.下单,下单量由冰山委托决定,按照对手价成交(买入用卖一价)。**下单后立即撤销(所以会看到很多撤销失败的单子,正常)**
|
||||
5.再次循环
|
||||
|
||||
|
||||
## 策略参数
|
||||
|
||||

|
||||
|
||||
- Trade_symbols:交易的币种,需要自己根据研究平台自己筛选,也可以加入BTC
|
||||
- Trade_value:山寨币做空持有价值,需要根据自己投入的总资金决定,可通过研究环境的回测看杠杆的大小
|
||||
- Adjust_value: 合约价值(USDT计价)调整偏离值,过大调整较慢,不能低于20,否则会达不到最小成交
|
||||
- Ice_value:冰山委托价值,同样不能低于20,实际下单选Adjust_value/Ice_value 中较小的那一个
|
||||
- Reset:重置历史数据
|
||||
|
||||
|
||||
## 策略风险
|
||||
|
||||
注意到如果某个币走出了独立的行情,比如相对于指数上涨了几倍,将会在该币种上积累大量的做空仓位,同样的大幅下跌也会使得策略大量做多。可限制开仓量或者止损不再交易。
|
||||
|
||||
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|Trade_symbols|IOST,TRX,XLM,QTUM,DASH,ADA,XMR,ZEC,ATOM,IOTA,NEO,ONT,XRP,VET,EOS,ETC,LTC|交易的币种|
|
||||
|Trade_value|50|每偏离指数1%持有价值|
|
||||
|Adjust_value|20|合约价值调整偏离值|
|
||||
|Ice_value|20|冰山委托订单的大小|
|
||||
|Log_profit_interval|600|Log总权益间隔s|
|
||||
|Interval|5|休眠时间s|
|
||||
|Reset|false|重置历史数据|
|
||||
|Max_amount|300|最大成交量,超过则不买入|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
//向上偏离最大的币的索引
|
||||
var highIndex=0;
|
||||
//向下偏离最大的币的索引
|
||||
var lowIndex=0;
|
||||
|
||||
var trade_symbols = Trade_symbols.split(',')
|
||||
var symbols = trade_symbols
|
||||
var index = 1 //指数
|
||||
if(trade_symbols.indexOf('BTC')<0){
|
||||
symbols = trade_symbols.concat(['BTC'])
|
||||
}
|
||||
var update_profit_time = 0
|
||||
var assets = {}
|
||||
var trade_info = {}
|
||||
var exchange_info = HttpQuery('https://fapi.binance.com/fapi/v1/exchangeInfo')
|
||||
if(!exchange_info){
|
||||
Log('无法连接网络')
|
||||
return
|
||||
}
|
||||
exchange_info = JSON.parse(exchange_info)
|
||||
for (var i=0; i<exchange_info.symbols.length; i++){
|
||||
if(symbols.indexOf(exchange_info.symbols[i].baseAsset) > -1){
|
||||
assets[exchange_info.symbols[i].baseAsset] = {amount:0, hold_price:0, value:0, bid_price:0, ask_price:0,
|
||||
btc_price:0, btc_change:1,btc_diff:0,
|
||||
realised_profit:0, margin:0, unrealised_profit:0}
|
||||
trade_info[exchange_info.symbols[i].baseAsset] = {minQty:parseFloat(exchange_info.symbols[i].filters[1].minQty),
|
||||
priceSize:parseInt((Math.log10(1.1/parseFloat(exchange_info.symbols[i].filters[0].tickSize)))),
|
||||
amountSize:parseInt((Math.log10(1.1/parseFloat(exchange_info.symbols[i].filters[1].stepSize))))
|
||||
}
|
||||
}
|
||||
}
|
||||
assets.USDT = {unrealised_profit:0, margin:0, margin_balance:0, total_balance:0, leverage:0, update_time:0}
|
||||
|
||||
function updateAccount(){ //更新账户和持仓
|
||||
var account = exchange.GetAccount()
|
||||
var pos = exchange.GetPosition()
|
||||
if (account == null || pos == null ){
|
||||
Log('update account time out')
|
||||
return
|
||||
}
|
||||
assets.USDT.update_time = Date.now()
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
assets[trade_symbols[i]].margin = 0
|
||||
assets[trade_symbols[i]].unrealised_profit = 0
|
||||
assets[trade_symbols[i]].hold_price = 0
|
||||
assets[trade_symbols[i]].amount = 0
|
||||
assets[trade_symbols[i]].unrealised_profit = 0
|
||||
}
|
||||
for(var j=0; j<account.Info.positions.length; j++){
|
||||
var pair = account.Info.positions[j].symbol
|
||||
var coin = pair.slice(0,pair.length-4)
|
||||
if(symbols.indexOf(coin) < 0){continue}
|
||||
assets[coin].margin = parseFloat(account.Info.positions[j].initialMargin) + parseFloat(account.Info.positions[j].maintMargin)
|
||||
assets[coin].unrealised_profit = parseFloat(account.Info.positions[j].unrealizedProfit)
|
||||
}
|
||||
assets.USDT.margin = _N(parseFloat(account.Info.totalInitialMargin) + parseFloat(account.Info.totalMaintMargin),2)
|
||||
assets.USDT.margin_balance = _N(parseFloat(account.Info.totalMarginBalance),2)
|
||||
assets.USDT.total_balance = _N(parseFloat(account.Info.totalWalletBalance),2)
|
||||
assets.USDT.unrealised_profit = _N(parseFloat(account.Info.totalUnrealizedProfit),2)
|
||||
assets.USDT.leverage = _N(assets.USDT.margin/assets.USDT.total_balance,2)
|
||||
|
||||
if(pos.length > 0){
|
||||
pos = JSON.parse(exchange.GetRawJSON())
|
||||
for(var k=0; k<pos.length; k++){
|
||||
var pair = pos[k].symbol
|
||||
var coin = pair.slice(0,pair.length-4)
|
||||
if(symbols.indexOf(coin) < 0){continue}
|
||||
assets[coin].hold_price = parseFloat(pos[k].entryPrice)
|
||||
assets[coin].amount = parseFloat(pos[k].positionAmt)
|
||||
assets[coin].unrealised_profit = parseFloat(pos[k].unRealizedProfit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateIndex(){ //更新指数
|
||||
var init_prices = {}
|
||||
if(!_G('init_prices') || Reset){
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
init_prices[trade_symbols[i]] = (assets[trade_symbols[i]].ask_price+assets[trade_symbols[i]].bid_price)/(assets.BTC.ask_price+assets.BTC.bid_price)
|
||||
}
|
||||
Log('保存启动时的价格')
|
||||
_G('init_prices',init_prices)
|
||||
}else{
|
||||
init_prices = _G('init_prices')
|
||||
var temp = 0
|
||||
|
||||
highIndex=0;
|
||||
lowIndex=0;
|
||||
var highChange;var lowChange;
|
||||
//本次计算找出最大偏离的高低分
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
assets[trade_symbols[i]].btc_price = (assets[trade_symbols[i]].ask_price+assets[trade_symbols[i]].bid_price)/(assets.BTC.ask_price+assets.BTC.bid_price)
|
||||
if(!init_prices[trade_symbols[i]]){
|
||||
Log('添加新的币种',trade_symbols[i])
|
||||
init_prices[trade_symbols[i]] = assets[trade_symbols[i]].btc_price
|
||||
_G('init_prices',init_prices)
|
||||
}
|
||||
assets[trade_symbols[i]].btc_change = _N(assets[trade_symbols[i]].btc_price/init_prices[trade_symbols[i]],4)
|
||||
if(i==0){
|
||||
highChange=assets[trade_symbols[i]].btc_change;
|
||||
lowChange=assets[trade_symbols[i]].btc_change;
|
||||
}
|
||||
|
||||
if(highChange<assets[trade_symbols[i]].btc_change){
|
||||
highChange=assets[trade_symbols[i]].btc_change;
|
||||
highIndex=i;
|
||||
}
|
||||
if(lowChange>assets[trade_symbols[i]].btc_change){
|
||||
lowChange=assets[trade_symbols[i]].btc_change;
|
||||
lowIndex=i;
|
||||
}
|
||||
}
|
||||
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
assets[trade_symbols[i]].btc_price = (assets[trade_symbols[i]].ask_price+assets[trade_symbols[i]].bid_price)/(assets.BTC.ask_price+assets.BTC.bid_price)
|
||||
assets[trade_symbols[i]].btc_change = _N(assets[trade_symbols[i]].btc_price/init_prices[trade_symbols[i]],4)
|
||||
if(i!=lowIndex&&i!=highIndex){ //去掉高低分的影响
|
||||
temp += assets[trade_symbols[i]].btc_change
|
||||
}
|
||||
}
|
||||
//因为去掉了最高最低分,所以减2
|
||||
index = _N(temp/(trade_symbols.length-2), 4)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function updateTick(){ //更新行情
|
||||
var ticker = HttpQuery('https://fapi.binance.com/fapi/v1/ticker/bookTicker')
|
||||
try {
|
||||
ticker = JSON.parse(ticker)
|
||||
}catch(e){
|
||||
Log('get ticker time out')
|
||||
return
|
||||
}
|
||||
for(var i=0; i<ticker.length; i++){
|
||||
var pair = ticker[i].symbol
|
||||
var coin = pair.slice(0,pair.length-4)
|
||||
if(symbols.indexOf(coin) < 0){continue}
|
||||
assets[coin].ask_price = parseFloat(ticker[i].askPrice)
|
||||
assets[coin].bid_price = parseFloat(ticker[i].bidPrice)
|
||||
assets[coin].ask_value = _N(assets[coin].amount*assets[coin].ask_price, 2)
|
||||
assets[coin].bid_value = _N(assets[coin].amount*assets[coin].bid_price, 2)
|
||||
}
|
||||
updateIndex()
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
assets[trade_symbols[i]].btc_diff = _N(assets[trade_symbols[i]].btc_change - index, 4)
|
||||
}
|
||||
}
|
||||
|
||||
function trade(symbol, dirction, value){ //交易
|
||||
if(Date.now()-assets.USDT.update_time > 10*1000){
|
||||
Log('更新账户延时,不交易')
|
||||
return
|
||||
}
|
||||
var price = dirction == 'sell' ? assets[symbol].bid_price : assets[symbol].ask_price
|
||||
var amount = _N(Math.min(value,Ice_value)/price, trade_info[symbol].amountSize)
|
||||
if(amount <= trade_info[symbol].minQty){
|
||||
Log(symbol, '合约价值偏离或冰山委托订单的大小设置过小,达不到最小成交, 至少需要: ', _N(trade_info[symbol].minQty*price,0))
|
||||
return
|
||||
}
|
||||
exchange.IO("currency", symbol+'_'+'USDT')
|
||||
exchange.SetContractType('swap')
|
||||
exchange.SetDirection(dirction)
|
||||
var f = dirction == 'buy' ? 'Buy' : 'Sell'
|
||||
var id = exchange[f](price, amount, symbol)
|
||||
if(id){
|
||||
exchange.CancelOrder(id) //订单会立即撤销
|
||||
}
|
||||
}
|
||||
|
||||
function updateStatus(){ //状态栏信息
|
||||
var table = {type: 'table', title: '交易对信息更新',
|
||||
cols: ['Symbol', 'amount', 'hold_price', 'price', 'diff', 'value', 'margin', 'unrealised_profit'],
|
||||
rows: []}
|
||||
var infoList;
|
||||
for (var i=0; i<symbols.length; i++){
|
||||
var price = _N((assets[symbols[i]].ask_price + assets[symbols[i]].bid_price)/2, trade_info[symbols[i]].priceSize)
|
||||
var value = _N((assets[symbols[i]].ask_value + assets[symbols[i]].bid_value)/2, 2)
|
||||
if(i==lowIndex){
|
||||
infoList = [symbols[i]+"Low", assets[symbols[i]].amount, assets[symbols[i]].hold_price, price, assets[symbols[i]].btc_diff, value, _N(assets[symbols[i]].margin,3), _N(assets[symbols[i]].unrealised_profit,3)]
|
||||
}else if(i==highIndex){
|
||||
infoList = [symbols[i]+"High", assets[symbols[i]].amount, assets[symbols[i]].hold_price, price, assets[symbols[i]].btc_diff, value, _N(assets[symbols[i]].margin,3), _N(assets[symbols[i]].unrealised_profit,3)]
|
||||
}else{
|
||||
infoList = [symbols[i], assets[symbols[i]].amount, assets[symbols[i]].hold_price, price, assets[symbols[i]].btc_diff, value, _N(assets[symbols[i]].margin,3), _N(assets[symbols[i]].unrealised_profit,3)]
|
||||
}
|
||||
table.rows.push(infoList)
|
||||
}
|
||||
var logString = _D() + ' ' + JSON.stringify(assets.USDT) + ' Index:' + index + '\n'
|
||||
LogStatus(logString + '`' + JSON.stringify(table) + '`')
|
||||
|
||||
if(Date.now()-update_profit_time > Log_profit_interval*1000){
|
||||
LogProfit(_N(assets.USDT.margin_balance,3))
|
||||
update_profit_time = Date.now()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onTick(){ //策略逻辑部分
|
||||
for(var i=0; i<trade_symbols.length; i++){
|
||||
var symbol = trade_symbols[i]
|
||||
if(assets[symbol].ask_price == 0){ continue }
|
||||
var aim_value = -Trade_value * _N(assets[symbol].btc_diff/0.01,1)
|
||||
|
||||
if(i!=lowIndex&&i!=highIndex){ //高低分的货币不交易
|
||||
if(aim_value - assets[symbol].ask_value > Adjust_value&&assets[symbol].ask_value<Max_amount){
|
||||
trade(symbol,'buy', aim_value - assets[symbol].ask_value)
|
||||
}
|
||||
if(aim_value - assets[symbol].bid_value < -Adjust_value&&assets[symbol].bid_value<Max_amount){
|
||||
trade(symbol,'sell', -(aim_value - assets[symbol].bid_value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
while(true){
|
||||
updateAccount()
|
||||
updateTick()
|
||||
onTick()
|
||||
updateStatus()
|
||||
Sleep(Interval*1000)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/196445
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-09 21:51:18
|
||||
@@ -1,190 +0,0 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
暴雪网格-7*24小时交易机器人
|
||||
|
||||
> 策略作者
|
||||
|
||||
红色的雪
|
||||
|
||||
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|base_price|3|基础价格-中线价格|
|
||||
|sale_buy_diff|0.03|买卖差价,卖出的价格- 买入的价格|
|
||||
|two_distance|0.01|递增、递减的时候,每个格子的距离|
|
||||
|trade_amount|2|每次交易的数量|
|
||||
|sale_price_max|5|卖出的上限价格|
|
||||
|buy_price_min|true|买入的上限价格|
|
||||
|
||||
|
||||
> 源码 (python)
|
||||
|
||||
``` python
|
||||
#!python2
|
||||
# -*- coding:utf-8 -*-
|
||||
'''
|
||||
主要是使用发明者量化API进行网格买卖,当前只支持单品网格买卖
|
||||
'''
|
||||
from time import sleep
|
||||
import datetime,copy
|
||||
|
||||
sale_price_list = [] #卖出的价格列表
|
||||
buy_price_list = [] #买入的价格列表
|
||||
|
||||
class fmz_market():
|
||||
def get_data_depth(self):
|
||||
data_depth = exchange.GetDepth()
|
||||
return data_depth
|
||||
|
||||
|
||||
|
||||
#检查当前是否可以进行买卖操作
|
||||
def make_trade_check(self,symbol):
|
||||
trade_infor = {'price':0,'trade_type':''}
|
||||
#进行买卖列表判断,先更新交易记录列表
|
||||
trade_price_list = self.get_trade_price_list(symbol)
|
||||
sale_price_list = trade_price_list[0]
|
||||
buy_price_list = trade_price_list[1]
|
||||
#获取深度数据
|
||||
data_depth = self.get_data_depth()
|
||||
#买单列表:
|
||||
data_depth_bids = data_depth.Bids[0]
|
||||
#卖单列表:
|
||||
data_depth_asks = data_depth.Asks[0]
|
||||
#如果买入记录不为空
|
||||
sale_buy_diff_now = two_distance*len(sale_price_list) if len(sale_price_list) >0 else sale_buy_diff
|
||||
sale_buy_diff_sale = two_distance if len(sale_price_list) > 0 else sale_buy_diff
|
||||
# sale_price_last = float(sale_price_list[len(sale_price_list)-1]) if len(sale_price_list) >0 else base_price
|
||||
# buy_price_last = float(buy_price_list[len(buy_price_list)-1]) if len(buy_price_list) >0 else base_price
|
||||
sale_price_last = float(sale_price_list[0]) if len(sale_price_list) > 0 and float(sale_price_list[0]) > base_price else base_price
|
||||
buy_price_last = float(buy_price_list[0]) if len(buy_price_list) > 0 and float(buy_price_list[0]) < base_price else base_price
|
||||
#判断当前价格是否满足 卖出的价格请求
|
||||
if float(data_depth_bids.Price) - sale_price_last > sale_buy_diff_sale and float(data_depth_bids.Amount) > trade_amount * 1.5:
|
||||
Log("当前卖价:",str(data_depth_bids.Price),"订单中最高卖价:",str(sale_price_last),"生成卖单")
|
||||
trade_infor['price'] = float(data_depth_bids.Price)
|
||||
trade_infor['trade_type'] = 'sale'
|
||||
#判断当前价格是否满足 买入的价格请求
|
||||
if buy_price_last - float(data_depth_asks.Price) > sale_buy_diff_now and float(data_depth_bids.Amount) > trade_amount * 1.5:
|
||||
#Log("当前买价:", str(data_depth_asks.Price), "订单中最高买价:", str(sale_price_last),"生成买单")
|
||||
trade_infor['price'] = float(data_depth_bids.Price)
|
||||
trade_infor['trade_type'] = 'buy'
|
||||
#判断当前价格是否破格,破格则置空买卖信息
|
||||
if float(data_depth_bids.Price) - sale_price_max > 0 or buy_price_min - float(data_depth_asks.Price) > 0:
|
||||
trade_infor['price'] = 0
|
||||
trade_infor['trade_type'] = ''
|
||||
timestr = (datetime.datetime.now()).strftime('%Y-%m-%d %H:%M:%S')
|
||||
Log(trade_infor,"...time:",timestr)
|
||||
if trade_infor['price'] != 0:
|
||||
#Log(trade_infor,"...time:",timestr)
|
||||
pass
|
||||
return trade_infor
|
||||
|
||||
#根据委托信息生成买卖价格列表
|
||||
def get_trade_price_list(self,symbol):
|
||||
sale_list = []
|
||||
buy_list = []
|
||||
#获取所有的交易记录,根据不同的类型 分配到 买卖列表中
|
||||
orders = exchange.GetOrders()
|
||||
for i in range(len(orders)):
|
||||
if orders[i].Type == 1:
|
||||
sale_price = float(orders[i].Price)
|
||||
sale_price_bak = copy.deepcopy(sale_price)
|
||||
sale_list.append(sale_price_bak)
|
||||
if orders[i].Type == 0:
|
||||
buy_price = float(orders[i].Price)
|
||||
buy_price_bak = copy.deepcopy(buy_price)
|
||||
buy_list.append(buy_price_bak)
|
||||
#判断为0的数组进处理
|
||||
if len(sale_list) == 0:
|
||||
for i in range(len(buy_list)):
|
||||
sale_list.append(float('%.6f' % (buy_list[i] + sale_buy_diff)))
|
||||
if len(buy_list) == 0:
|
||||
for i in range(len(sale_list)):
|
||||
buy_list.append(float('%.6f' % (sale_list[i] - sale_buy_diff)))
|
||||
trade_price_list = [sale_list,buy_list]
|
||||
return trade_price_list
|
||||
|
||||
#网格交易入口:
|
||||
def grid_trade_start(self,symbol):
|
||||
#进行状态获取,上涨/下跌
|
||||
# trend_status = self.kline_trend_check(symbol)
|
||||
|
||||
#获取可否进行交易
|
||||
trade_infor = self.make_trade_check(symbol)
|
||||
#进行判断是否交易,判断是否可以卖出
|
||||
# if trend_status == 'is_up' and trade_infor['price'] > 0 and trade_infor['trade_type'] == 'sale':
|
||||
if trade_infor['price'] > 0 and trade_infor['trade_type'] == 'sale':
|
||||
buy_price = float('%.6f' % (trade_infor['price'] - sale_buy_diff))
|
||||
#调用下单功能,先调用卖,再调用买
|
||||
order_id = exchange.Sell(trade_infor['price'], trade_amount)
|
||||
#Log('order_id:',order_id)
|
||||
#检查下单是否成功,不成功,则直接返回
|
||||
if order_id is None:
|
||||
Log("下单失败,等待600s继续.........")
|
||||
sleep(600)
|
||||
return 0
|
||||
#检查主交易是否成功:判断卖单单是否交易成功,交易成功则进行买单下单
|
||||
for i in range(100):
|
||||
sale_orders = exchange.GetOrder(order_id)
|
||||
#Log('sale_orders:',sale_orders)
|
||||
if sale_orders.Status == 1:
|
||||
#如果卖单已成交,则进行买单提交
|
||||
exchange.Buy(buy_price, trade_amount)
|
||||
return 0
|
||||
sleep(10)
|
||||
#如果循环1000次还未成交,则取消订单
|
||||
exchange.CancelOrder(order_id)
|
||||
# 进行判断是否交易,判断是否可以买入
|
||||
if trade_infor['price'] > 0 and trade_infor['trade_type'] == 'buy':
|
||||
sale_price = float('%.6f' % (trade_infor['price'] + sale_buy_diff))
|
||||
# 调用下单功能,先调用买入,再调用卖出
|
||||
order_id = exchange.Buy(trade_infor['price'], trade_amount)
|
||||
# 检查下单是否成功,不成功,则直接返回
|
||||
if order_id is None:
|
||||
#Log("下单失败,等待600s继续.........")
|
||||
sleep(600)
|
||||
return 0
|
||||
# 检查主交易是否成功:判断买单是否交易成功,交易成功则进行卖单下单
|
||||
for i in range(100):
|
||||
buy_orders = exchange.GetOrder(order_id)
|
||||
if buy_orders.Status == 1:
|
||||
# 如果买单已成交,则进行卖单提交
|
||||
exchange.Sell(sale_price, trade_amount)
|
||||
return 0
|
||||
sleep(10)
|
||||
# 如果循环1000次还未成交,则取消订单
|
||||
exchange.CancelOrder(order_id)
|
||||
|
||||
#进行循环调用
|
||||
def grid_trade_cycle(self,symbol):
|
||||
cycle_num = 0
|
||||
while(True):
|
||||
timestr = (datetime.datetime.now()).strftime('%H%M%S')
|
||||
self.grid_trade_start(symbol)
|
||||
sleep(10)
|
||||
cycle_num = cycle_num + 1
|
||||
if cycle_num % 100 == 0:
|
||||
account_infor = exchange.GetAccount()
|
||||
Log("当前用户的账号信息:%s,....当前已循环检查次数:%s"%(account_infor,str(cycle_num)))
|
||||
|
||||
def main():
|
||||
Log(exchange.GetAccount())
|
||||
Log("测试")
|
||||
fmz_market_instances = fmz_market()
|
||||
fmz_market_instances.grid_trade_cycle(100)
|
||||
#order_id = exchange.Sell(10000,1)
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/175807
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-12-06 14:54:19
|
||||
+463
@@ -0,0 +1,463 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
期货做市高频交易策略
|
||||
|
||||
> 策略作者
|
||||
|
||||
John
|
||||
|
||||
> 策略描述
|
||||
|
||||
这个策略属于期货的做市策略
|
||||
会对盘口价格造成影响
|
||||
所以不适合大资金.
|
||||
策略为研究市场行为而写, 实盘请用小资金测试.
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|ContractType|0|合约类型: 周|月|季: 周|月|季|
|
||||
|MLevel|0|杠杆大小: 5倍|10倍|20倍: 5倍|10倍|20倍|
|
||||
|DepthBuy|5|买单深度|
|
||||
|DepthSell|5|卖单深度|
|
||||
|Interval|500|出错重试间隔(毫秒)|
|
||||
|LoopInterval|2|轮询间隔(秒)|
|
||||
|SlidePrice|0.01|滑点|
|
||||
|MinDiff|0.5|盘口最小差价|
|
||||
|KeepStocksRate|true|保证金保留倍数|
|
||||
|Lot|80|开仓手数|
|
||||
|LotCover|200|平仓手数|
|
||||
|StopLoss|3|最大亏损波动(元)|
|
||||
|StopProfit|0.5|目标利润点(元)|
|
||||
|OpType|0|操作类型: 做多|做空|自动: 做多|做空|自动|
|
||||
|EMA_Fast|7|EMA快线周期|
|
||||
|EMA_Slow|12|EMA慢线周期|
|
||||
|EMAInterval|50|EMA检测间隔(秒)|
|
||||
|WaitType|0|K线收集完成前: 做多|做空|等待: 做多|做空|等待|
|
||||
|DisableLog|false|关闭订单跟踪|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
var DO_IDLE = 0;
|
||||
var DO_LONG = 1;
|
||||
var DO_SHORT = 2;
|
||||
|
||||
var _MarginLevel = [5, 10, 20][MLevel];
|
||||
var _ContractType = ["week", "month", "quarter"][ContractType];
|
||||
var _CurrentDirection = [DO_LONG, DO_SHORT, DO_IDLE][OpType != 2 ? OpType : WaitType];
|
||||
var _isAuto = OpType == 2;
|
||||
var _IsBitVC = false;
|
||||
var _Is796 = false;
|
||||
var _IsOKCoin = false;
|
||||
var _MinAmount = 0;
|
||||
var _Fee = 0.0003;
|
||||
|
||||
function _N(v, precision) {
|
||||
if (typeof(precision) != 'number') {
|
||||
precision = _IsOKCoin ? 3 : 2;
|
||||
}
|
||||
var d = parseFloat(v.toFixed(Math.max(10, precision + 5)));
|
||||
s = d.toString().split(".");
|
||||
if (s.length < 2 || s[1].length <= precision) {
|
||||
return d;
|
||||
}
|
||||
|
||||
var b = Math.pow(10, precision);
|
||||
return Math.floor(d * b) / b;
|
||||
}
|
||||
|
||||
function GetOrders() {
|
||||
var orders = null;
|
||||
while (!(orders = exchange.GetOrders())) {
|
||||
Sleep(Interval);
|
||||
}
|
||||
return orders;
|
||||
}
|
||||
|
||||
function CancelPendingOrders(orderType) {
|
||||
while (true) {
|
||||
var orders = GetOrders();
|
||||
var count = 0;
|
||||
if (typeof(orderType) != 'undefined') {
|
||||
for (var i = 0; i < orders.length; i++) {
|
||||
if (orders[i].Type == orderType) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
count = orders.length;
|
||||
}
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var j = 0; j < orders.length; j++) {
|
||||
if (typeof(orderType) == 'undefined' || (orderType == orders[j].Type)) {
|
||||
exchange.CancelOrder(orders[j].Id, orders[j]);
|
||||
if (j < (orders.length - 1)) {
|
||||
Sleep(Interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function GetPosition(orderType) {
|
||||
var positions;
|
||||
while (!(positions = exchange.GetPosition())) {
|
||||
Sleep(Interval);
|
||||
}
|
||||
for (var i = 0; i < positions.length; i++) {
|
||||
if (positions[i].ContractType == _ContractType && positions[i].Type == orderType) {
|
||||
return positions[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function GetAccount() {
|
||||
var account;
|
||||
while (!(account = exchange.GetAccount())) {
|
||||
Sleep(Interval);
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
function GetLimit() {
|
||||
var ticker;
|
||||
while (!(ticker = exchange.GetTicker())) {
|
||||
Sleep(Interval);
|
||||
}
|
||||
if (IsVirtual()) {
|
||||
return {
|
||||
high: ticker.Sell + 100,
|
||||
low: ticker.Buy - 100
|
||||
};
|
||||
}
|
||||
var js = exchange.GetRawJSON();
|
||||
try {
|
||||
var obj = JSON.parse(js);
|
||||
return {
|
||||
high: parseFloat(obj.limit_highest_price),
|
||||
low: parseFloat(obj.limit_lowest_price)
|
||||
};
|
||||
} catch (e) {
|
||||
Log(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function GetDepth() {
|
||||
var depth;
|
||||
while (true) {
|
||||
depth = exchange.GetDepth();
|
||||
if (depth && depth.Asks.length > 0 && depth.Bids.length > 0 && depth.Asks[0].Price > depth.Bids[0].Price) {
|
||||
break;
|
||||
}
|
||||
Sleep(Interval);
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
var LastOpenPrice = 0;
|
||||
var LastCoverPrice = 0;
|
||||
var LastHoldPrice = 0;
|
||||
var LastRecord = null;
|
||||
var LastEMATime = 0;
|
||||
|
||||
function GetDirection() {
|
||||
if (OpType != 2) {
|
||||
return _CurrentDirection;
|
||||
}
|
||||
var n = new Date().getTime();
|
||||
if ((n - LastEMATime) < (EMAInterval * 1000)) {
|
||||
return _CurrentDirection;
|
||||
}
|
||||
LastEMATime = n;
|
||||
|
||||
var records = exchange.GetRecords();
|
||||
if (!records || records.length < (EMA_Slow + 3)) {
|
||||
return _CurrentDirection;
|
||||
}
|
||||
var newLast = records[records.length - 1];
|
||||
if ((!LastRecord) || (LastRecord.Time == newLast.Time && LastRecord.Close == newLast.Close)) {
|
||||
if (!LastRecord) {
|
||||
LastRecord = newLast;
|
||||
}
|
||||
return _CurrentDirection;
|
||||
}
|
||||
LastRecord = newLast;
|
||||
|
||||
var emaFast = TA.EMA(records, EMA_Fast);
|
||||
var emaSlow = TA.EMA(records, EMA_Slow);
|
||||
return emaFast[emaFast.length - 1] >= emaSlow[emaSlow.length - 1] ? DO_LONG : DO_SHORT;
|
||||
}
|
||||
|
||||
function GetPrice() {
|
||||
var buyPrice = 0;
|
||||
var buyAmount = 0;
|
||||
var sellPrice = 0;
|
||||
var sellAmount = 0;
|
||||
var depth = GetDepth();
|
||||
for (var i = 0; i < depth.Asks.length; i++) {
|
||||
sellAmount += depth.Asks[i].Amount;
|
||||
if (sellAmount >= DepthSell) {
|
||||
sellPrice = depth.Asks[i].Price;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < depth.Bids.length; i++) {
|
||||
buyAmount += depth.Bids[i].Amount;
|
||||
if (buyAmount >= DepthBuy) {
|
||||
buyPrice = depth.Bids[i].Price;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var diff = MinDiff - (sellPrice - buyPrice);
|
||||
if (diff >= 0) {
|
||||
buyPrice = buyPrice - (diff / 2);
|
||||
sellPrice = sellPrice + (diff / 2);
|
||||
} else {
|
||||
buyPrice += SlidePrice;
|
||||
sellPrice -= SlidePrice;
|
||||
}
|
||||
return {
|
||||
buy: buyPrice,
|
||||
sell: sellPrice
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function updateProfit() {
|
||||
var account = GetAccount();
|
||||
if (_IsBitVC) {
|
||||
try {
|
||||
var obj = JSON.parse(exchange.GetRawJSON());
|
||||
LogProfit(obj.dynamicRights, 'Stocks:', account.Stocks, 'FrozenStocks:', account.FrozenStocks);
|
||||
} catch (e) {
|
||||
Log(e);
|
||||
}
|
||||
} else {
|
||||
LogProfit(account.Stocks + account.FrozenStocks);
|
||||
}
|
||||
}
|
||||
|
||||
var _prePositions = 0;
|
||||
|
||||
function onTick() {
|
||||
var price = GetPrice();
|
||||
|
||||
if (_IsBitVC) {
|
||||
var limit = GetLimit();
|
||||
if (limit) {
|
||||
price.sell = Math.max(Math.min(price.sell, limit.high), limit.low);
|
||||
price.buy = Math.min(Math.max(price.buy, limit.low), limit.high);
|
||||
}
|
||||
}
|
||||
|
||||
var openFunc = _CurrentDirection == DO_LONG ? exchange.Buy : exchange.Sell;
|
||||
var openDirection = _CurrentDirection == DO_LONG ? "buy" : "sell";
|
||||
var openTradeType = _CurrentDirection == DO_LONG ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
|
||||
|
||||
var coverFunc = _CurrentDirection == DO_LONG ? exchange.Sell : exchange.Buy;
|
||||
var coverDirection = _CurrentDirection == DO_LONG ? "closebuy" : "closesell";
|
||||
var coverTradeType = _CurrentDirection == DO_LONG ? ORDER_TYPE_SELL : ORDER_TYPE_SELL;
|
||||
|
||||
var openPrice = _N(_CurrentDirection == DO_LONG ? price.buy : price.sell);
|
||||
var coverPrice = _N(_CurrentDirection == DO_LONG ? price.sell : price.buy);
|
||||
|
||||
var op = 0;
|
||||
var isFighting = false;
|
||||
var marketCoverPrice = coverPrice;
|
||||
|
||||
if (LastHoldPrice > 0 && LastCoverPrice > 0) {
|
||||
if (_CurrentDirection == DO_LONG) {
|
||||
isFighting = (LastHoldPrice - coverPrice) <= StopLoss;
|
||||
if (isFighting) {
|
||||
coverPrice = _N((LastHoldPrice * (1 + _Fee)) + StopProfit);
|
||||
} else {
|
||||
isFighting = true;
|
||||
}
|
||||
} else {
|
||||
isFighting = (coverPrice - LastHoldPrice) <= StopLoss;
|
||||
if (isFighting) {
|
||||
coverPrice = _N((LastHoldPrice * (1 - _Fee)) - StopProfit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (LastHoldPrice > 0) {
|
||||
LogStatus(_CurrentDirection == DO_LONG ? "多仓" : "空仓", isFighting ? "僵持中.." : "止损中..", "持仓均价: ", _N(LastHoldPrice), "盘口平仓价:", marketCoverPrice, isFighting ? "#0000ff" : "#ff0000");
|
||||
} else {
|
||||
LogStatus(_CurrentDirection == DO_LONG ? "做多" : "做空", "开仓价: ", openPrice, "平仓价:", marketCoverPrice, "抢盘中... #428bca");
|
||||
}
|
||||
|
||||
if (openPrice != LastOpenPrice) {
|
||||
op = 1;
|
||||
}
|
||||
|
||||
if (coverPrice != LastCoverPrice) {
|
||||
op = op == 0 ? 2 : 3;
|
||||
}
|
||||
|
||||
if (op == 3) {
|
||||
CancelPendingOrders();
|
||||
} else if (op != 0) {
|
||||
CancelPendingOrders(op == 1 ? openTradeType : coverTradeType);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
var position = 0;
|
||||
|
||||
var hold = GetPosition(openTradeType);
|
||||
if (hold) {
|
||||
position = hold.Amount;
|
||||
LastHoldPrice = hold.Price;
|
||||
} else {
|
||||
LastHoldPrice = 0;
|
||||
}
|
||||
|
||||
var coverAmount = _N(Math.min(position, _IsBitVC ? (LotCover * 100) : LotCover));
|
||||
if (!_Is796) {
|
||||
coverAmount = parseInt(coverAmount);
|
||||
}
|
||||
if (_IsBitVC) {
|
||||
coverAmount = parseInt(parseInt(coverAmount / 100) * 100);
|
||||
}
|
||||
if ((op == 2 || op == 3) && (coverAmount >= _MinAmount)) {
|
||||
exchange.SetDirection(coverDirection);
|
||||
if (coverFunc(coverPrice, coverAmount, "上次平仓价格", LastCoverPrice) > 0) {
|
||||
LastCoverPrice = coverPrice;
|
||||
}
|
||||
}
|
||||
|
||||
if (position == 0 && _prePositions != 0) {
|
||||
updateProfit();
|
||||
_prePositions = 0;
|
||||
}
|
||||
|
||||
var d = GetDirection();
|
||||
if (_prePositions == 0) {
|
||||
_prePositions = position;
|
||||
}
|
||||
|
||||
|
||||
if (_isAuto) {
|
||||
if (d != _CurrentDirection) {
|
||||
if (position == 0 && GetOrders().length == 0) {
|
||||
Log(d == DO_LONG ? "开始做多" : "开始做空");
|
||||
_CurrentDirection = d;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var account = GetAccount();
|
||||
var canUsedStocks = account.Stocks;
|
||||
if (KeepStocksRate > 0) {
|
||||
canUsedStocks = _N(Math.max(account.Stocks - _N((position * (_IsOKCoin ? 10 : 1) / _MarginLevel / (_Is796 ? 1 : openPrice)) * KeepStocksRate), 0) / KeepStocksRate);
|
||||
}
|
||||
var openAmount = 0;
|
||||
if (_IsBitVC) {
|
||||
openAmount = parseInt(parseInt(Math.min(((canUsedStocks * _MarginLevel * (openPrice * 0.9)) / 100), Lot)) * 100);
|
||||
} else if (_IsOKCoin) {
|
||||
openAmount = parseInt(Math.min(((canUsedStocks * _MarginLevel * (openPrice * 0.9)) / 10), Lot));
|
||||
} else {
|
||||
openAmount = _N(Math.min(canUsedStocks * _MarginLevel, Lot));
|
||||
}
|
||||
if ((op == 1 || op == 3) && (openAmount >= _MinAmount)) {
|
||||
exchange.SetDirection(openDirection);
|
||||
if (openFunc(openPrice, openAmount, "上次建仓价格", LastOpenPrice) > 0) {
|
||||
LastOpenPrice = openPrice;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onexit() {
|
||||
CancelPendingOrders();
|
||||
Log("Exit");
|
||||
}
|
||||
|
||||
function main() {
|
||||
var eName = exchange.GetName();
|
||||
|
||||
if (eName.indexOf("Futures") == -1) {
|
||||
throw "该策略为期货专用策略";
|
||||
}
|
||||
if (_IsOKCoin && _MarginLevel == 5) {
|
||||
throw "OKCoin期货只支持10倍或20倍杠杆";
|
||||
}
|
||||
if (DisableLog) {
|
||||
EnableLog(false);
|
||||
}
|
||||
|
||||
_IsBitVC = eName.indexOf("BitVC") != -1;
|
||||
_Is796 = eName.indexOf("796") != -1;
|
||||
_IsOKCoin = eName.indexOf("OKCoin") != -1;
|
||||
|
||||
if (_IsBitVC) {
|
||||
_MinAmount = 100;
|
||||
} else if (_IsOKCoin) {
|
||||
_MinAmount = 1;
|
||||
} else {
|
||||
_MinAmount = exchange.GetMinStock();
|
||||
}
|
||||
|
||||
exchange.SetRate(1);
|
||||
Log(exchange.GetName(), GetAccount());
|
||||
CancelPendingOrders();
|
||||
LoopInterval = Math.max(LoopInterval, 1);
|
||||
EMAInterval = Math.max(EMAInterval, 1);
|
||||
if (_IsBitVC || _IsOKCoin) {
|
||||
Lot = parseInt(Math.max(1, Lot));
|
||||
LotCover = parseInt(Math.max(1, LotCover));
|
||||
if (Lot < 1 || LotCover < 1) {
|
||||
throw "手数最少为1, BitVC一手相当于100元人民币, OKCoin相当于10美金.";
|
||||
}
|
||||
}
|
||||
KeepStocksRate = parseInt(KeepStocksRate);
|
||||
Log("预留", KeepStocksRate, "倍的保证金");
|
||||
|
||||
if (_IsOKCoin && ContractType == 0) {
|
||||
_ContractType = "this_week";
|
||||
}
|
||||
if (!_IsOKCoin && ContractType == 1) {
|
||||
throw "只有OKCoin期货支持月全约";
|
||||
}
|
||||
if (_Is796 && ContractType != 0) {
|
||||
throw "796只支持周合约类型";
|
||||
}
|
||||
exchange.SetContractType(_ContractType);
|
||||
exchange.SetMarginLevel(_MarginLevel);
|
||||
|
||||
if (OpType == 2) {
|
||||
Log("开始收集K线信息, 请耐心等待.");
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (_CurrentDirection == DO_IDLE) {
|
||||
_CurrentDirection = GetDirection();
|
||||
} else {
|
||||
onTick();
|
||||
}
|
||||
Sleep(LoopInterval * 1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/194180
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-15 14:13:39
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
期货看穿式测试
|
||||
|
||||
> 策略作者
|
||||
|
||||
Hukybo
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
num = 0
|
||||
|
||||
function onTick() {
|
||||
if (num < 1) {
|
||||
_C(exchange.SetContractType, "rb2001")
|
||||
account = _C(exchange.GetAccount).Info.AccountID
|
||||
Log("账号:", account);
|
||||
Log("AppID:", "client_fmz_1.0");
|
||||
records = _C(exchange.GetRecords)
|
||||
k = records[records.length - 1].Close
|
||||
exchange.SetDirection("buy");
|
||||
id = exchange.Buy(k + 2, 1)
|
||||
Log("sessionid:", id);
|
||||
Log("登陆时间:", Date());
|
||||
|
||||
Log("账号:", account);
|
||||
records = _C(exchange.GetRecords)
|
||||
k = records[records.length - 1].Close
|
||||
for (x = 0; x < 5; x++) {
|
||||
exchange.SetDirection("buy");
|
||||
id = exchange.Buy(k + 2, 1)
|
||||
Log("sessionid:", id);
|
||||
Sleep(1000)
|
||||
}
|
||||
|
||||
Log("账号:", account);
|
||||
for (y = 0; y < 5; y++) {
|
||||
exchange.SetDirection("buy");
|
||||
id = exchange.Buy(k - 2, 1)
|
||||
Log("sessionid:", id);
|
||||
Sleep(1000)
|
||||
}
|
||||
orders = _C(exchange.GetOrders)
|
||||
for (z = 0; z < 5; z++) {
|
||||
Log("账号:", account);
|
||||
Log("合约:", "rb1910");
|
||||
var ordersid = orders[z].Id
|
||||
if (exchange.CancelOrder(ordersid)) {
|
||||
Log("订单:" + ordersid + " 已撤单")
|
||||
}
|
||||
}
|
||||
num++
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
while (true) {
|
||||
onTick();
|
||||
Sleep(1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/152670
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-09-09 16:12:19
|
||||
+3
-2
@@ -9,7 +9,8 @@ Zero
|
||||
|
||||
> 策略描述
|
||||
|
||||
BotVS支持策略同时展现多个图表, 这是一个简单的例子. 如果不能正常展示, 请清空浏览器缓存后刷新
|
||||
平台支持策略同时展现多个图表, 这是一个简单的例子
|
||||
具体使用方法可参考API文档中的Chart部分 https://www.fmz.com/api#chart
|
||||
|
||||
|
||||
|
||||
@@ -156,4 +157,4 @@ https://www.fmz.com/strategy/38203
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-01-24 17:33:12
|
||||
2020-04-08 14:53:51
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
测试现货买卖操作(分限价、市价两种情况)
|
||||
|
||||
> 策略作者
|
||||
|
||||
himandy
|
||||
|
||||
> 策略描述
|
||||
|
||||
回测火币数据,以及在wexapp模拟盘交易,都得到类似下面的运行结果:
|
||||
|
||||
如果限价下单,那么exchange.Buy(6840, 5)就是以6840的价格买入5个btc。
|
||||
如果市价下单,那么exchange.Buy(-1, 5)就是以市价、花费5个usdt买入btc。
|
||||
|
||||
策略代码:https://www.fmz.com/m/edit-strategy/191349
|
||||
|
||||
2020年4月5日
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
/*backtest
|
||||
start: 2020-01-01 00:00:00
|
||||
end: 2020-04-01 00:00:00
|
||||
period: 1d
|
||||
exchanges: [{"eid":"Huobi","currency":"BTC_USD","balance":1000000,"stocks":0}]
|
||||
*/
|
||||
|
||||
var id, order, buyAmount, lastPrice;
|
||||
|
||||
function main() {
|
||||
Log(exchange.GetAccount());
|
||||
|
||||
lastPrice = parseInt(exchange.GetTicker().Last);
|
||||
id = exchange.Buy(lastPrice + 50, 5); // 限价买入5个BTC,买入价是当前最新价格+50
|
||||
Log(order = exchange.GetOrder(id));
|
||||
buyAmount = parseFloat(order.DealAmount);
|
||||
Log(exchange.GetAccount());
|
||||
|
||||
Sleep(1000);
|
||||
last_price = parseInt(exchange.GetTicker().Last);
|
||||
id = exchange.Sell(lastPrice - 50, buyAmount); // 限价卖出5个BTC,卖出价是当前最新价格-50
|
||||
Log(order = exchange.GetOrder(id));
|
||||
Log(exchange.GetAccount());
|
||||
|
||||
Sleep(1000);
|
||||
id = exchange.Buy(-1, 5); // 市价买入BTC,成交量是5个usdt
|
||||
Sleep(1000);
|
||||
Log(order = exchange.GetOrder(id));
|
||||
buyAmount = parseFloat(order.DealAmount);
|
||||
Log(exchange.GetAccount());
|
||||
|
||||
Sleep(1000);
|
||||
id = exchange.Sell(-1, buyAmount); // 市价卖出BTC,成交量是刚才买入的BTC
|
||||
Sleep(1000);
|
||||
Log(order = exchange.GetOrder(id));
|
||||
Log(exchange.GetAccount());
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/191349
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-06 12:15:13
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
混合图表的一个复杂例子
|
||||
|
||||
> 策略作者
|
||||
|
||||
Zero
|
||||
|
||||
> 策略描述
|
||||
|
||||
|
||||
更多需求请直接查询 [Highchart官方](https://www.highcharts.com/) 或 [Highchart第三方中文文档](https://www.highcharts.com.cn/)
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
/*backtest
|
||||
start: 2020-03-11 00:00:00
|
||||
end: 2020-04-09 23:59:00
|
||||
period: 1d
|
||||
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
|
||||
*/
|
||||
|
||||
var chartCfg = {
|
||||
subtitle: {
|
||||
text: "subtitle",
|
||||
},
|
||||
yAxis: [{
|
||||
height: "40%",
|
||||
lineWidth: 2,
|
||||
title: {
|
||||
text: 'PnL',
|
||||
},
|
||||
tickPixelInterval: 20,
|
||||
minorGridLineWidth: 1,
|
||||
minorTickWidth: 0,
|
||||
opposite: true,
|
||||
labels: {
|
||||
align: "right",
|
||||
x: -3,
|
||||
}
|
||||
}, {
|
||||
title: {
|
||||
text: 'Profit',
|
||||
},
|
||||
top: "42%",
|
||||
height: "18%",
|
||||
offset: 0,
|
||||
lineWidth: 2
|
||||
}, {
|
||||
title: {
|
||||
text: 'Vol',
|
||||
},
|
||||
top: '62%',
|
||||
height: '18%',
|
||||
offset: 0,
|
||||
lineWidth: 2
|
||||
}, {
|
||||
title: {
|
||||
text: 'Asset',
|
||||
},
|
||||
top: '82%',
|
||||
height: '18%',
|
||||
offset: 0,
|
||||
lineWidth: 2
|
||||
}],
|
||||
series: [{
|
||||
name: 'PnL',
|
||||
data: [],
|
||||
id: 'primary',
|
||||
tooltip: {
|
||||
xDateFormat: '%Y-%m-%d %H:%M:%S'
|
||||
},
|
||||
yAxis: 0
|
||||
}, {
|
||||
type: 'column',
|
||||
lineWidth: 2,
|
||||
name: 'Profit',
|
||||
data: [],
|
||||
yAxis: 1,
|
||||
}, {
|
||||
type: 'column',
|
||||
name: 'Trade',
|
||||
data: [],
|
||||
yAxis: 2
|
||||
}, {
|
||||
type: 'area',
|
||||
step: true,
|
||||
lineWidth: 0,
|
||||
name: 'Long',
|
||||
data: [],
|
||||
yAxis: 2
|
||||
}, {
|
||||
type: 'area',
|
||||
step: true,
|
||||
lineWidth: 0,
|
||||
name: 'Short',
|
||||
data: [],
|
||||
yAxis: 2
|
||||
}, {
|
||||
type: 'line',
|
||||
step: true,
|
||||
color: '#5b4b00',
|
||||
name: 'Asset',
|
||||
data: [],
|
||||
yAxis: 3
|
||||
}, {
|
||||
type: 'pie',
|
||||
innerSize: '70%',
|
||||
name: 'Random',
|
||||
data: [],
|
||||
center: ['3%', '6%'],
|
||||
size: '15%',
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
startAngle: -90,
|
||||
endAngle: 90,
|
||||
}],
|
||||
};
|
||||
|
||||
function main() {
|
||||
let c = Chart(chartCfg);
|
||||
let preTicker = null;
|
||||
while (true) {
|
||||
let t = exchange.GetTicker();
|
||||
|
||||
c.add(0, [t.Time, t.Last]); // PnL
|
||||
c.add(1, [t.Time, preTicker ? t.Last - preTicker.Last : 0]); // profit
|
||||
let r = Math.random();
|
||||
var pos = parseInt(t.Time/86400);
|
||||
c.add(2, [t.Time, pos/2]); // Vol
|
||||
c.add(3, [t.Time, r > 0.8 ? pos : null]); // Long
|
||||
c.add(4, [t.Time, r < 0.8 ? -pos : null]); // Short
|
||||
c.add(5, [t.Time, Math.random() * 100]); // Asset
|
||||
// update pie
|
||||
chartCfg.series[chartCfg.series.length-1].data = [
|
||||
["A", Math.random()*100],
|
||||
["B", Math.random()*100],
|
||||
];
|
||||
c.update(chartCfg)
|
||||
preTicker = t;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/196963
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-11 12:57:17
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
用FMZ的均线的交易方式PYTHON版本2
|
||||
|
||||
> 策略作者
|
||||
|
||||
a838899
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
> 源码 (python)
|
||||
|
||||
``` python
|
||||
'''
|
||||
用FMZ的均线的交易方式PYTHON版本
|
||||
交易对象时OKEX BTC_USDT
|
||||
方法:
|
||||
用公式计算的MA曲线数据、 用的FMZ的TA库
|
||||
用1小时的线周期,MA5上穿MA20 买入 MA5下穿MA20 卖出
|
||||
欢迎学习交流
|
||||
微信:su7178
|
||||
欢迎学习交流
|
||||
'''
|
||||
'''backtest
|
||||
start: 2020-03-13 00:00:00
|
||||
end: 2020-04-13 00:00:00
|
||||
period: 1h
|
||||
exchanges: [{"eid":"OKEX","currency":"BTC_USD"}]
|
||||
'''
|
||||
buy_m = 0
|
||||
sell_m = 0
|
||||
def do_trade(trade_type):
|
||||
nw_accont = _C(exchange.GetAccount)
|
||||
money = nw_accont.Balance
|
||||
B = nw_accont.Stocks
|
||||
if trade_type == "buy" and money >= 10:
|
||||
exchange.Buy(-1, money)
|
||||
global buy_m
|
||||
buy_m += 1
|
||||
Log("买入btc资金 : ", money,"买入btc数量 : ", B,"买入次数 : ",buy_m)
|
||||
elif trade_type == "sell" and B >= 0.001:
|
||||
exchange.Sell(-1, B)
|
||||
global sell_m
|
||||
sell_m += 1
|
||||
Log("卖出btc资金 : ", money,"卖出btc数量 : ", B,"卖出次数 : ",sell_m)
|
||||
else: return
|
||||
# 策略主函数
|
||||
def onTick():
|
||||
# exchange.SetContractType("quarter") # 订阅期货品种
|
||||
records = exchange.GetRecords(PERIOD_H1) # 获取K线数组
|
||||
# records_15 = records.
|
||||
if len(records) < 50: return
|
||||
close = records[len(records) - 1].Close
|
||||
ma_5 = TA.MA(records, 5)
|
||||
# Log(ma_5)
|
||||
ma_20 = TA.MA(records, 20)
|
||||
cross = 0
|
||||
cross = _Cross(ma_5, ma_20)
|
||||
if cross == -1:
|
||||
trade_type = "buy"
|
||||
do_trade(trade_type)
|
||||
elif cross == 1:
|
||||
trade_type = "sell"
|
||||
do_trade(trade_type)
|
||||
else: return
|
||||
# 程序入口
|
||||
def main():
|
||||
accont = _C(exchange.GetAccount)
|
||||
int_money = accont.Balance
|
||||
Log("初始资金 : ", int_money)
|
||||
int_B = accont.Stocks
|
||||
Log("初始BTC : ", int_B)
|
||||
while True: # 进入无限循环模式
|
||||
onTick() # 执行策略主函数
|
||||
Sleep(3000) # 休眠1秒
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/198397
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-14 14:56:42
|
||||
@@ -0,0 +1,55 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
简单的股票趋势跟踪交易策略
|
||||
|
||||
> 策略作者
|
||||
|
||||
Zero
|
||||
|
||||
> 策略描述
|
||||
|
||||
> 说明
|
||||
|
||||
|
||||
现阶段股票只支持回测, 选股功能下一步将加入,最后再进行实盘支持
|
||||
|
||||
|
||||
|
||||
> 源码 (麦语言)
|
||||
|
||||
``` pascal
|
||||
(*backtest
|
||||
start: 2016-03-01 09:00:00
|
||||
end: 2020-03-01 15:00:00
|
||||
period: 1d
|
||||
exchanges: [{"eid":"Futures_LTS","currency":"STOCK","minFee":0}]
|
||||
args: [["TradeAmount",100,126961],["ContractType","601398.SH",126961]]
|
||||
*)
|
||||
|
||||
M := 12; // 参数范围 1, 20
|
||||
N := 3; // 参数范围 1, 10
|
||||
SDEV := 2; // 参数范围 1, 10
|
||||
P := 16; // 参数范围 1, 20
|
||||
//该策略为趋势跟踪交易策略,适用较大周期,如日线。
|
||||
//该模型仅用作模型开发案例,依此入市,风险自负。
|
||||
////////////////////////////////////////////////////////
|
||||
//平移BOLL通道计算
|
||||
MID:=MA(C,N);//计算中轨
|
||||
TMP:=STD(C,M)*SDEV;//计算标准差
|
||||
DISPTOP:=REF(MID,P)+TMP;//平移BOLL通道上轨
|
||||
DISPBOTTOM:=REF(MID,P)-TMP;//平移BOLL通道下轨
|
||||
//系统入场
|
||||
H>=DISPTOP,BPK;
|
||||
L<=DISPBOTTOM,SPK;
|
||||
AUTOFILTER;
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/195935
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-04-08 13:41:15
|
||||
@@ -1,66 +0,0 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
简单预定买入后卖出机器人|Buy then Sell (ping-pong strategy)
|
||||
|
||||
> 策略作者
|
||||
|
||||
小草
|
||||
|
||||
> 策略描述
|
||||
|
||||
如题,可设定买入价格,买入成功后自动挂卖出价格卖出,
|
||||
|
||||
> 策略参数
|
||||
|
||||
|
||||
|
||||
|参数|默认值|描述|
|
||||
|----|----|----|
|
||||
|BUYPRICE|6200|buy price|
|
||||
|BUYAMOUNT|0.1|buy amount|
|
||||
|SELLPRICE|6400|sell price after buying|
|
||||
|Intervel|6|sleep time (second)|
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
function CancelPendingOrders() {
|
||||
var orders = _C(exchange.GetOrders);
|
||||
for (var j = 0; j < orders.length; j++) {
|
||||
exchange.CancelOrder(orders[j].Id, orders[j]);
|
||||
}
|
||||
}
|
||||
function main() {
|
||||
Log('robot starts to run')
|
||||
if(BUYPRICE >= SELLPRICE){
|
||||
throw 'check buy and sell price'
|
||||
}
|
||||
CancelPendingOrders()
|
||||
var account = _C(exchange.GetAccount)
|
||||
var init_account = account
|
||||
Log('account: ', account.Balance);
|
||||
if(account.Balance > BUYPRICE*BUYAMOUNT){
|
||||
exchange.Buy(BUYPRICE, BUYAMOUNT);
|
||||
}else{
|
||||
throw 'account balances is not enough'
|
||||
}
|
||||
while(true){
|
||||
account = _C(exchange.GetAccount)
|
||||
if(account.Stocks >= init_account.Stocks + 0.01){
|
||||
exchange.Sell(SELLPRICE, account.Stocks - init_account.Stocks)
|
||||
}
|
||||
Sleep(Intervel*1000)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/121228
|
||||
|
||||
> 更新时间
|
||||
|
||||
2019-10-22 14:57:26
|
||||
@@ -411,7 +411,7 @@ function fishing(orgAccount, fishCount) {
|
||||
exchange.SetDirection(BuyFirst ? "closebuy" : "closesell");
|
||||
}
|
||||
var coverId = CoverFunc(coverPrice, BuyFirst ? amountS : amountB, BuyFirst ? '完成买单:' : '完成卖单:', openPrice);
|
||||
if (coverId > 0) {
|
||||
if (coverId) {
|
||||
fishTable[idx] = STATE_WAIT_CLOSE;
|
||||
uuidTable[idx] = coverId;
|
||||
}
|
||||
@@ -420,7 +420,7 @@ function fishing(orgAccount, fishCount) {
|
||||
exchange.SetDirection(BuyFirst ? "buy" : "sell");
|
||||
}
|
||||
var openId = OpenFunc(openPrice, BuyFirst ? amountB : amountS);
|
||||
if (openId > 0) {
|
||||
if (openId) {
|
||||
fishTable[idx] = STATE_WAIT_COVER;
|
||||
uuidTable[idx] = openId;
|
||||
if (state == STATE_WAIT_CLOSE) {
|
||||
@@ -496,4 +496,4 @@ https://www.fmz.com/strategy/8474
|
||||
|
||||
> 更新时间
|
||||
|
||||
2018-06-05 15:09:40
|
||||
2020-04-06 18:47:43
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
获取最新疫情|Fetch nCoV News
|
||||
|
||||
> 策略作者
|
||||
|
||||
fmzero
|
||||
|
||||
> 策略描述
|
||||
|
||||
病毒无情,人间有爱。
|
||||
FMZ一直有情怀。
|
||||
丁香园一直做实时发布疫情发展情况,而我想通过FMZ的`HttpQuery`爬取其实时信息。并用Log(...&)推送至微信公众号上,用LogStatus展现信息(有人说可能没卵用)。
|
||||
这就是温度。
|
||||
如下代码未能实现正则表达式过滤信息,仅仅是一个半成品。望大神助力
|
||||
|
||||
[温度](https://www.fmz.com/strategy/182505)
|
||||
|
||||
|
||||
|
||||
|
||||
> 源码 (javascript)
|
||||
|
||||
``` javascript
|
||||
// 有温度的FMZ
|
||||
|
||||
var url = 'https://3g.dxy.cn/newh5/view/pneumonia'
|
||||
var newsRegex = "/<script id=\"getTimelineService\">.+?window.getTimelineService\s=\s({.+?)}catch\(e\){}<\/script>/im"
|
||||
var provinceRegex = "/<script id=\"getListByCountryTypeService1\">.+?window.getListByCountryTypeService1\s=\s(\[.+?])}catch\(e\){}<\/script>/im"
|
||||
|
||||
///////////////////////////////////////////
|
||||
function get_dxy_data() {
|
||||
var ret = HttpQuery(url)
|
||||
return ret
|
||||
}
|
||||
|
||||
function get_nCoV_news(content ) {
|
||||
var news = content.match(newsRegex)
|
||||
Log(news)
|
||||
}
|
||||
|
||||
function get_nCoV_province(content) {
|
||||
var ret = HttpQuery(url)
|
||||
var province = ret.search(provinceRegex)
|
||||
Log(province)
|
||||
|
||||
}
|
||||
|
||||
function main() {
|
||||
while(true) {
|
||||
Sleep(10 * 1000)
|
||||
var content = get_dxy_data()
|
||||
if(content === null) {
|
||||
continue
|
||||
}
|
||||
get_nCoV_news(content)
|
||||
get_nCoV_province(content)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/182505
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-01-24 14:23:36
|
||||
@@ -1,348 +0,0 @@
|
||||
|
||||
> 策略名称
|
||||
|
||||
通用协议(Python3.X版)——改编自CoinPark交易所通用协议 6.27 16:00 更新 关闭了SSL验证
|
||||
|
||||
> 策略作者
|
||||
|
||||
orienter
|
||||
|
||||
> 策略描述
|
||||
|
||||
## CoinPark通用协议Python2版本
|
||||
用途:可以使BotVs支持coinpark.cc交易所
|
||||
代码公开地址:https://www.fmz.com/strategy/101399
|
||||
运行地址
|
||||
|
||||
http://127.0.0.1:6667
|
||||
### 1.在托管者所在服务器运行插件
|
||||
|
||||
在保存成文件上传的托管者所在服务器,后台运行即可:
|
||||
|
||||
nohup python coinpark.py &
|
||||
也可以在运行时指定端口:
|
||||
|
||||
python coinpark.py 8866
|
||||
也可以当成一个普通机器人运行在模拟盘上,不收取费用。
|
||||
|
||||
### 2.在FMZ网站配置交易所
|
||||
|
||||
交易所配置如下:
|
||||

|
||||
由于账户没资产,未作详细测试,欢迎反馈Bug
|
||||
2018.6.26 15:57 更新,修改了Bug
|
||||
|
||||
|
||||
|
||||
> 源码 (python)
|
||||
|
||||
``` python
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
CoinPark通用协议,使用Python3.7
|
||||
运行地址:http://127.0.0.1:6667,端口可指定
|
||||
由于账户没资产,未作详细测试,欢迎反馈Bug
|
||||
|
||||
QQ:124404425
|
||||
|
||||
可以把通用协议当成普通机器人,运行在BotVs模拟盘即可,不收取费用
|
||||
为了使用IO函数,需要重载exchange里的rpc方法,js的例子如下:
|
||||
exchange.rpc = function(path, obj) {
|
||||
return exchange.IO("api","POST", path, "obj="+escape(JSON.stringify(obj)));
|
||||
}
|
||||
function main() {
|
||||
Log(exchange.rpc("/transfer", {cmd: "transfer/assets", body: {select:1}}));
|
||||
}
|
||||
'''
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
import json
|
||||
import urllib
|
||||
import urllib.request as urllib2
|
||||
import time
|
||||
import hmac
|
||||
import hashlib
|
||||
import random
|
||||
import ssl
|
||||
ssl._create_default_https_context = ssl._create_unverified_context
|
||||
|
||||
def httpGet(url):
|
||||
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
|
||||
req = urllib2.Request(url,headers=headers)
|
||||
response = urllib2.urlopen(req)
|
||||
return json.loads(response.read())
|
||||
|
||||
def getsign(data,secret):
|
||||
result = hmac.new(secret.encode("utf-8"), data.encode("utf-8"), hashlib.md5).hexdigest()
|
||||
return result
|
||||
|
||||
def httpPostWithSign(url, cmds, api_key, api_secret):
|
||||
headers = {'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'}
|
||||
s_cmds = json.dumps(cmds)
|
||||
sign = getsign(s_cmds,api_secret)
|
||||
req = urllib2.Request(url, urllib.urlencode({'cmds': s_cmds, 'apikey': api_key,'sign':sign}), headers=headers)
|
||||
response = urllib2.urlopen(req)
|
||||
return json.loads(response.read())
|
||||
|
||||
class MyExchange:
|
||||
|
||||
market_url = "https://api.coinpark.cc/v1/mdata"
|
||||
trade_url = "https://api.coinpark.cc/v1"
|
||||
kline_period = {1:'1min', 3:'3min', 5:'5min', 15:'15min', 30:'30min',\
|
||||
60:'1hour', 120:'2hour', 240:'4hour', 360:'6hour', \
|
||||
60*12:'12hour', 60*24:'day', 60*24*7:'week'}
|
||||
@staticmethod
|
||||
def GetTicker(symbol):
|
||||
url = MyExchange.market_url + "?cmd=ticker&pair=" + symbol
|
||||
raw_data = httpGet(url)
|
||||
if 'error' in raw_data.keys():
|
||||
return {'error':json.dumps(raw_data['error'],encoding="utf8", ensure_ascii=False)}
|
||||
ret_data = {"data": {"time": raw_data['result']['timestamp'], "buy": raw_data['result']['buy'],\
|
||||
"sell": raw_data['result']['sell'], "last": raw_data['result']['last'],\
|
||||
"high": raw_data['result']['high'], "low": raw_data['result']['low'],\
|
||||
"vol": raw_data['result']['vol']}}
|
||||
return ret_data
|
||||
@staticmethod
|
||||
def GetDepth(symbol):
|
||||
url = MyExchange.market_url + "?cmd=depth&size=10&pair=" + symbol
|
||||
raw_data = httpGet(url)
|
||||
if 'error' in raw_data.keys():
|
||||
return {'error':json.dumps(raw_data['error'],encoding="utf8", ensure_ascii=False)}
|
||||
ret_data = {"data" : {"time" : raw_data['result']['update_time'], "asks" : [], "bids" : []}}
|
||||
for bid in raw_data['result']['bids']:
|
||||
ret_data['data']['bids'].append([bid['price'],bid['volume']])
|
||||
for ask in raw_data['result']['asks']:
|
||||
ret_data['data']['asks'].append([ask['price'],ask['volume']])
|
||||
return ret_data
|
||||
@staticmethod
|
||||
def GetRecords(symbol, period):
|
||||
url = MyExchange.market_url + "?cmd=kline&size=200&period=%s&pair="%MyExchange.kline_period[period] + symbol
|
||||
raw_data = httpGet(url)
|
||||
if 'error' in raw_data.keys():
|
||||
return {'error':json.dumps(raw_data['error'],encoding="utf8", ensure_ascii=False)}
|
||||
ret_data = {"data": []}
|
||||
for kline in raw_data['result']:
|
||||
ret_data['data'].append([kline['time'], kline['open'], kline['high'],\
|
||||
kline['low'], kline['close'], kline['vol']])
|
||||
return ret_data
|
||||
@staticmethod
|
||||
def GetTrades(symbol):
|
||||
url = MyExchange.market_url + "?cmd=deals&size=50&pair=" + symbol
|
||||
raw_data = httpGet(url)
|
||||
if 'error' in raw_data.keys():
|
||||
return {'error':json.dumps(raw_data['error'],encoding="utf8", ensure_ascii=False)}
|
||||
ret_data = {"data":[]}
|
||||
for trade in raw_data["result"]:
|
||||
ret_data["data"].append({"id":trade["id"], "time":trade["time"], \
|
||||
"price":trade["price"], "amount":trade["amount"],"type":"buy" if int(trade["side"])==1 else "sell"})
|
||||
return ret_data
|
||||
@staticmethod
|
||||
def GetAccount(api_key, api_secret):
|
||||
url = MyExchange.trade_url + "/transfer"
|
||||
cmds = [{"cmd": "transfer/assets", "body": {"select":1}}]
|
||||
raw_data = httpPostWithSign(url, cmds, api_key, api_secret)
|
||||
if 'error' in raw_data.keys():
|
||||
return {'error':json.dumps(raw_data['error'],encoding="utf8", ensure_ascii=False)}
|
||||
ret_data = {"data": []}
|
||||
if "assets_list" in raw_data["result"][0]["result"].keys():
|
||||
for asset in raw_data["result"][0]["result"]["assets_list"]:
|
||||
ret_data["data"].append({"currency":asset["coin_symbol"], \
|
||||
"free":asset["balance"], "frozen":asset["freeze"]})
|
||||
ret_data["raw"] = raw_data["result"]
|
||||
return ret_data
|
||||
@staticmethod
|
||||
def Trade(api_key, api_secret, pair, order_type, order_side, price, amount):
|
||||
url = MyExchange.trade_url + "/orderpending"
|
||||
cmds = [{
|
||||
'cmd':"orderpending/trade",
|
||||
'index': random.randint(0,2000),
|
||||
'body':{
|
||||
'pair':pair,
|
||||
'account_type':0,
|
||||
'order_type':order_type,
|
||||
'order_side':order_side,
|
||||
'price':price,
|
||||
'amount':amount,
|
||||
}
|
||||
}]
|
||||
if order_type==1:
|
||||
cmds['money'] = amount
|
||||
raw_data = httpPostWithSign(url, cmds, api_key, api_secret)
|
||||
if 'error' in raw_data.keys():
|
||||
return {'error':json.dumps(raw_data['error'],encoding="utf8", ensure_ascii=False)}
|
||||
ret_data = {"data": {'id':raw_data['result'][0]['result']}}
|
||||
return ret_data
|
||||
@staticmethod
|
||||
def CancelOrder(api_key, api_secret, orders_id):
|
||||
url = MyExchange.trade_url + "/orderpending"
|
||||
cmds = [{
|
||||
'cmd':"orderpending/cancelTrade",
|
||||
'index': random.randint(0,2000),
|
||||
'body':{'orders_id':orders_id}
|
||||
}]
|
||||
raw_data = httpPostWithSign(url, cmds, api_key, api_secret)
|
||||
if 'error' in raw_data.keys():
|
||||
return {'error':json.dumps(raw_data['error'],encoding="utf8", ensure_ascii=False)}
|
||||
ret_data = {"data":True}
|
||||
try:
|
||||
result = raw_data['result'].encode('utf8')
|
||||
except:
|
||||
ret_data = {"data":False}
|
||||
ret_data['raw'] = raw_data
|
||||
return ret_data
|
||||
@staticmethod
|
||||
def GetOrder(api_key, api_secret, orders_id):
|
||||
url = MyExchange.trade_url + "/orderpending"
|
||||
cmds = [{
|
||||
'cmd':"orderpending/order",
|
||||
'index': random.randint(0,2000),
|
||||
'body':{'id':orders_id}
|
||||
}]
|
||||
raw_data = httpPostWithSign(url, cmds, api_key, api_secret)
|
||||
if 'error' in raw_data.keys():
|
||||
return {'error':json.dumps(raw_data['error'],encoding="utf8", ensure_ascii=False)}
|
||||
status = 'open'
|
||||
if not raw_data['result'][0]['result']:
|
||||
return {"error":'Id not found'}
|
||||
if int(raw_data['result'][0]['result']['status'])==3:
|
||||
status = 'closed'
|
||||
if int(raw_data['result'][0]['result']['status'])==5:
|
||||
status = 'canceled'
|
||||
ret_data = {
|
||||
"data": {
|
||||
"id": raw_data['result'][0]['result']['id'],
|
||||
"amount": raw_data['result'][0]['result']['amount'],
|
||||
"price": raw_data['result'][0]['result']['price'],
|
||||
"status": status,
|
||||
"deal_amount": raw_data['result'][0]['result']['deal_amount'],
|
||||
"type": "buy" if raw_data['result'][0]['result']['order_side']==1 else "sell",
|
||||
"avg_price": 0,
|
||||
}
|
||||
}
|
||||
ret_data['raw'] = raw_data
|
||||
return ret_data
|
||||
@staticmethod
|
||||
def GetOrders(api_key, api_secret, pair):
|
||||
url = MyExchange.trade_url + "/orderpending"
|
||||
cmds = [{
|
||||
'cmd':"orderpending/orderPendingList",
|
||||
'body':{
|
||||
'pair':pair,
|
||||
'page':1,
|
||||
'size':50
|
||||
}
|
||||
}]
|
||||
raw_data = httpPostWithSign(url, cmds, api_key, api_secret)
|
||||
if 'error' in raw_data.keys():
|
||||
return {'error':json.dumps(raw_data['error'],encoding="utf8", ensure_ascii=False)}
|
||||
ret_data = {"data":[]}
|
||||
for order in raw_data["result"][0]["result"]["items"]:
|
||||
status = 'open'
|
||||
if int(order['status'])==3:
|
||||
status = 'closed'
|
||||
if int(order['status'])==5:
|
||||
status = 'canceled'
|
||||
ret_data["data"].append(
|
||||
{
|
||||
"id": order['id'],
|
||||
"amount": order['amount'],
|
||||
"price": order['price'],
|
||||
"status": status,
|
||||
"deal_amount": order['deal_amount'],
|
||||
"type": "buy" if order['order_side']==1 else "sell",
|
||||
}
|
||||
)
|
||||
ret_data['raw'] = raw_data
|
||||
return ret_data
|
||||
@staticmethod
|
||||
def IO(api_key, api_secret, path, params):
|
||||
url = MyExchange.trade_url + path
|
||||
cmds = [json.loads(str(urllib.unquote(params['obj'])))]
|
||||
raw_data = httpPostWithSign(url, cmds, api_key, api_secret)
|
||||
if 'error' in raw_data.keys():
|
||||
return {'error':json.dumps(raw_data['error'],encoding="utf8", ensure_ascii=False)}
|
||||
return {"data":raw_data}
|
||||
|
||||
class Server(BaseHTTPRequestHandler):
|
||||
|
||||
def do_HEAD(self):
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.end_headers()
|
||||
|
||||
def do_POST(self):
|
||||
|
||||
self.data_string = self.rfile.read(int(self.headers['Content-Length'])).decode()
|
||||
data =json.loads(self.data_string.replace("'", '"'))
|
||||
sent_data = {}
|
||||
if data['method'] == "ticker":
|
||||
symbol = data['params']['symbol'].upper()
|
||||
sent_data = MyExchange.GetTicker(symbol)
|
||||
elif data['method'] == "depth":
|
||||
symbol = data['params']['symbol'].upper()
|
||||
sent_data = MyExchange.GetDepth(symbol)
|
||||
elif data['method'] == "records":
|
||||
symbol = data['params']['symbol'].upper()
|
||||
period = data['params']['period']
|
||||
sent_data = MyExchange.GetRecords(symbol, int(period))
|
||||
elif data['method'] == "trades":
|
||||
symbol = data['params']['symbol'].upper()
|
||||
sent_data = MyExchange.GetTrades(symbol)
|
||||
elif data['method'] == "accounts":
|
||||
access_key = data["access_key"]
|
||||
secret_key = data["secret_key"]
|
||||
sent_data = MyExchange.GetAccount(access_key, secret_key)
|
||||
elif data['method'] == "trade":
|
||||
access_key = data["access_key"]
|
||||
secret_key = data["secret_key"]
|
||||
pair = data['params']['symbol'].upper()
|
||||
order_side = 1 if data['params']['type'] == 'buy' else 2
|
||||
price = data['params']['price']
|
||||
order_type = 2 if price > 0 else 1
|
||||
amount = data['params']['amount']
|
||||
sent_data = MyExchange.Trade(access_key, secret_key, pair, order_type, order_side, price, amount)
|
||||
elif data['method'] == "cancel":
|
||||
access_key = data["access_key"]
|
||||
secret_key = data["secret_key"]
|
||||
orders_id = int(data['params']['id'])
|
||||
sent_data = MyExchange.CancelOrder(access_key, secret_key, orders_id)
|
||||
elif data['method'] == "order":
|
||||
access_key = data["access_key"]
|
||||
secret_key = data["secret_key"]
|
||||
orders_id = int(data['params']['id'])
|
||||
sent_data = MyExchange.GetOrder(access_key, secret_key, orders_id)
|
||||
elif data['method'] == "orders":
|
||||
access_key = data["access_key"]
|
||||
secret_key = data["secret_key"]
|
||||
pair = data['params']['symbol'].upper()
|
||||
sent_data = MyExchange.GetOrders(access_key, secret_key, pair)
|
||||
elif data['method'][:2] == "__":
|
||||
access_key = data["access_key"]
|
||||
secret_key = data["secret_key"]
|
||||
path = data["method"].split('_')[-1]
|
||||
params = data["params"]
|
||||
sent_data = MyExchange.IO(access_key, secret_key, path, params)
|
||||
|
||||
self.do_HEAD()
|
||||
self.wfile.write(json.dumps(sent_data).encode())
|
||||
|
||||
def run(server_class=HTTPServer, handler_class=Server, port=6667):
|
||||
server_address = ('', port)
|
||||
httpd = server_class(server_address, handler_class)
|
||||
print('Starting http server...')
|
||||
httpd.serve_forever()
|
||||
|
||||
if __name__ == "__main__":
|
||||
from sys import argv
|
||||
if len(argv) == 2:
|
||||
run(port=int(argv[1]))
|
||||
else:
|
||||
run()
|
||||
```
|
||||
|
||||
> 策略出处
|
||||
|
||||
https://www.fmz.com/strategy/188813
|
||||
|
||||
> 更新时间
|
||||
|
||||
2020-03-06 20:11:54
|
||||
+3
-4
@@ -11,9 +11,8 @@ Zero
|
||||
|
||||
> 尝个鲜
|
||||
|
||||
* 完全兼容文华麦语言语法
|
||||
* 基于发明者强大的低层, 完全支持数字货币现货期货与国内大宗商品期货
|
||||
* 兼容工作会一直持续, 目前兼容度90%大部分策略可以直接运行, 无需修改
|
||||
* 自动移仓换月, 真实反映主力合约切换过程
|
||||
* API文档 https://www.fmz.com/bbs-topic/2569
|
||||
|
||||
>语言增强
|
||||
@@ -32,7 +31,7 @@ scope.TEST = function(obj) {
|
||||
上一个收盘价放大100倍:TEST(REF(C, 1)); // 鼠标移动到回测的K线上就会提示变量值
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
> 策略参数
|
||||
|
||||
@@ -93,4 +92,4 @@ https://www.fmz.com/strategy/126968
|
||||
|
||||
> 更新时间
|
||||
|
||||
2018-12-24 13:35:31
|
||||
2020-04-10 11:40:47
|
||||
|
||||
Reference in New Issue
Block a user