Name
收藏价格振幅均线-3年33万倍收益
Author
盯盘狗 - 策略出租
Source (javascript)
/*backtest
start: 2020-01-01 00:00:00
end: 2023-03-24 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT","fee":[0.018,0.036]}]
*/
//地址https://www.fmz.com/strategy/405725
const QuotePrecision = 10;//下单价格精度
const BasePrecision = 10;//下单量精度
const long = 'long';//做多-期货
const closelong = 'closelong';//做多-平仓
const short = 'short';//做空-期货
const closeshort = 'closeshort';//做空-平仓
const isTest = true;//是不是币安测试API
const minPriceNum = 2;//保留几位小数点(买卖价格最低设置类似1660.17usdt)
const marginLevel = 1;//杆杠大小
let lastKLineTime = 0;//最后一个结束的k线时间戳(比如现在10:30,那就是9点时间戳)
const priceAdd = 0.01;//交易时改变1%设置价格
let lastOrderTime = 0;//最后一次下订单时间戳-秒
const serviceCharge = 0.0004;//手续费0.04%
let signalData = {};//订单数据-用于订单交易成功时使用
let maintMarginPro = 0.005;//维持保证金比率0.50%
let maintMarginNum = 0;//维持保证金速算额 (USDT)
const cfgName1 = "k线图表";
var registerInfo = {};
var chart = null;
var arrCfg = [];
const maxBufferLen = 10;//
let beginMoney = 0;//启动时的总资产
let exchangeNum = 0;//本次交易次数(开仓+平仓算2次)
const lastNumPro_MarketPrice = 0.02;//因为用市价不好把握价格,空间留大点
const buyMinPro = 0.02;//剩余钱不到总钱2%,不操作,避免小额交易
let period = 0;//K线周期-秒
const atrNum = 6;
const stopKLineNum = 0;//出现止损,休息()个K线
const pp = 12;//数值越大,格子差距越小
const StopGrid = 16;//止损网格价格倍数
let lastStopTime = 0;
const checkTime = 1500;//每隔多少秒检测一下代码-1小时
let lastCheckTime = 0;//上次检测时间戳-秒
let currency;
let nowMarkPrice = 0;
let nowPosition;
let nowAccount;
function main() {
testLog("调用main");
let extension = {
layout: 'single',//single:图表不会叠加(不会以分页标签方式显示),会单独显示(平铺显示),默认为分组 'group'
col: 12,//设置图表的页面宽度,1-12
height: '700px'//图表的高度
}
Sleep(1000);
while (true) {
setNowData();
let bars = _C(exchange.GetRecords);//只能获得最近300个周期的数据?
PlotMultRecords(bars, cfgName1, "k线", extension);//画k线图
drawLine(bars);//画sma线,策略
Sleep(1000 * 10);//每秒运行下代码
}
}
/** 活动最新数据 */
function setNowData() {
setMarkPrice();
setNowPosition();
setNowAccount();
}
// 初始化函数
function init() {
_CDelay(1000 * 10);// 调整_C()函数重试时间间隔为n秒
testLog("初始化!");
// 设置精度(小数点位数,回测不支持)
exchange.SetPrecision(QuotePrecision, BasePrecision);
exchange.SetContractType("swap");//永续合约
if (isTest) {
exchange.SetBase("https://testnet.binancefuture.com");// 切换为测试的地址
}
let arr = exchange.GetCurrency().split('_');//ETH_USDT=>ETH_USDT
currency = arr[0] + arr[1];
Log('交易对:' + currency);
setNowData();
log_ticker("初始化");
log_account("初始化");
beginMoney = getAllMoney();
chart = Chart(arrCfg);
period = _C(exchange.GetPeriod);//K线周期
let periodTxt = getTimeTxt(period);
testLog("K线周期:" + periodTxt);
}
/** 画sma均线 */
function drawLine(bars) {
// 使用现货交易所对象测试,获取K线数据。如果使用期货交易所对象测试,需要先设置合约
if (!bars) {
return;
}
if (lastKLineTime == 0) {//第一次启动,从头开始
var begin = atrNum - 1;
} else {
begin = bars.length - 2;//理论上只看最新一个数据就行,但为了防止误差,看2个
}
if (begin < 0) {
return;
}
let atr = talib.ATR(bars, atrNum);//计算平均价格振幅
for (let i = begin; i < bars.length; i++) {
let bar = bars[i];
if (bar.Time <= lastKLineTime) {//已经计算、画图过的
continue;
}
let avg_pra = atr[i] / bar.Close
let hl2 = (bar.High + bar.Low) / 2;
//计算网格
let grid = hl2 * avg_pra / pp;
let p1 = hl2 + grid * StopGrid;
let p2 = hl2 + grid * 2;
let p0 = hl2;
let p3 = hl2 - grid * 2;
let p4 = hl2 - grid * StopGrid;
//------------画k线图等
if (i < bars.length - 1) {
lastKLineTime = bar.Time;
//最后一条数据不完整,结束画
PlotMultLine(cfgName1, "p1", p1, bar.Time, '#FF9800');
PlotMultLine(cfgName1, "p2", p2, bar.Time, '#265ec5');
PlotMultLine(cfgName1, "p0", p0, bar.Time, '#7c7c7c');
PlotMultLine(cfgName1, "p3", p3, bar.Time, '#265ec5');
PlotMultLine(cfgName1, "p4", p4, bar.Time, '#FF9800');
continue;
}
//------------以最后一条数据获得的sma值进行交易判断(for循环只进最后一次)
// ======================== 策略操作 - 开始 ============================
if (hasOrder()) {//有未完成的订单
return;
}
let nowTime = getNowTime();
if ((lastCheckTime + checkTime) <= nowTime) {
lastCheckTime = nowTime;//每隔多少秒检测一下代码
} else {
return;
}
if ((lastStopTime + stopKLineNum * period * 1000) > bar.Time) {
return;
}
//只做多
if (bar.Close < p4) {
let funTxt = 'Fun_止损';
let isSuccess = ExchangeFun(closelong, funTxt);
if (isSuccess) {
lastStopTime = bar.Time;
}
} else if (bar.Close < p3) {
let funTxt = 'Fun_做多';
ExchangeFun(long, funTxt);
} else if (bar.Close > p2) {
let funTxt = 'Fun_止盈';
ExchangeFun(closelong, funTxt);
}
}
}
/** 有没有未完成的订单 */
function hasOrder() {
let orders = _C(exchange.GetOrders);//获取所有未完成的订单
if (!orders) {
return true;
}
if (orders.length == 0 && signalData && signalData.FunTxt) {
let order = _C(exchange.GetOrder, signalData.Id);
if (order) {
exchangeNum++;
let nowTime = getNowTime();
let timeTxtxt = getTimeTxt(nowTime - lastOrderTime);
let txt = signalData.FunTxt + ',报价:' + order.Price + ',均价:' + order.AvgPrice
+ ',量:' + order.Amount + '%' + ",次数:" + exchangeNum
+ ",Action:" + signalData.Action + ",耗时:" + timeTxtxt;
Log("end订单成功----" + txt);
log_account("end----");
PlotMultFlag(cfgName1, "flag1", new Date().getTime(), txt, signalData.FunTxt);
signalData = {};
}
}
if (orders.length > 0) {
return true;
} else {
return false;
}
}
//做多做空等实际功能
function ExchangeFun(action, funTxt, percent = 1) {
let amount;
let tradeInfo;
let markPrice = getMarkPrice();//标记价格
if (action == long || action == closeshort) {//买
var price = markPrice * (1 + priceAdd);
} else if (action == short || action == closelong) {//卖
price = markPrice * (1 - priceAdd);
}
price = _floor(markPrice, minPriceNum);
let min = getMinNum();//eth最小操作0.001个
if (action == long) {//做多
if (!canBuy()) {//避免小额交易
return false;
}
amount = getBuyNum(price, 0);//购买多少个eth
if (amount >= min) {
beginTrans();
exchange.SetMarginLevel(marginLevel);//设置杆杠大小
exchange.SetDirection("buy");
tradeInfo = exchange.Buy(-1, amount);//市价
}
} else if (action == short) {//做空
if (!canBuy()) {//避免小额交易
return false;
}
amount = getBuyNum(price, 1);//做空卖多少个eth
if (amount >= min) {
beginTrans();
exchange.SetMarginLevel(marginLevel);//设置杆杠大小
exchange.SetDirection("sell");
tradeInfo = exchange.Sell(-1, amount);
}
} else if (action == closelong) {//平仓-做多
amount = getCoinNum(0) * percent;//卖掉多少个期货eth
if (amount >= min) {
beginTrans();
exchange.SetDirection("closebuy");
tradeInfo = exchange.Sell(-1, amount);
}
} else if (action == closeshort) {//平仓-做空
amount = getCoinNum(1) * percent;
if (amount >= min) {
beginTrans();
exchange.SetDirection("closesell");
tradeInfo = exchange.Buy(-1, amount);
}
}
if (tradeInfo) {
signalData = { 'Action': action, 'Id': tradeInfo, 'FunTxt': funTxt };
log_ticker("end----,订单Action:" + action + ",price:" + price);
return true;
}
return false;
}
/** 开始交易 */
function beginTrans() {
lastOrderTime = getNowTime();
log_account("bengin----");
}
function log_account(txt) {
let acc = GetAcc();
let markPrice = getMarkPrice();
testLog(txt + ",可用余额:" + acc.Balance + ",标记价格:" + markPrice + ",账户:" + JSON.stringify(acc));
let position = getNowPosition();
if (position.length > 0) {
let data = position[0];//默认持仓就1个
let typeTxt = '';
if (data.Type == 0) {
typeTxt = '做多';
} else if (data.Type == 1) {
typeTxt = '做空';
}
let leverageTxt = '';
if (data.Info) {//回测时没有?
leverageTxt = ",杠杆:" + data.Info.leverage;
}
testLog("持仓信息 eth:" + data.Amount + ",Price:" + data.Price
// , ",利润:", data.Profit
+ ",Type:" + typeTxt + leverageTxt + ",详情:" + JSON.stringify(data));
}
}
/** 获得账号信息 */
function GetAcc() {
return nowAccount;
}
/** 设置账号信息 */
function setNowAccount() {
let acc = _C(exchange.GetAccount);
if (acc.Info) {
acc.Info.assets = null;
acc.Info.positions = null;
}
nowAccount = acc;
}
function log_ticker(txt) {
testLog(txt);
}
/** 预计可以购买多少个eth 0做多,1做空 */
function getBuyNum(price, type) {
let acc = GetAcc();
let balance = acc.Balance;//有多少usdt
if (balance < 100000) {
maintMarginPro = 0.005;//维持保证金比率
maintMarginNum = 0;//维持保证金速算额 (USDT)
} else if (balance < 250000) {
maintMarginPro = 0.0065;
maintMarginNum = 150;
} else if (balance < 2000000) {
maintMarginPro = 0.01;
maintMarginNum = 1025;
} else if (balance < 10000000) {
maintMarginPro = 0.02;
maintMarginNum = 21025;
} else {
//暂不处理
}
balance -= maintMarginNum;
let markPrice = getMarkPrice();//标记价格
let lossType;//开仓亏损:订单方向
//尽量多买,避免小额多次交易
if (type == 0) {//做多
lossType = 1;
} else if (type == 1) {//做空
lossType = -1;
}
if (type == 0) {//做多
var usePrice = price;
} else if (type == 1) {//做空
usePrice = Math.max(price, markPrice);
}
// 开仓亏损=合约数量* 绝对值{min[0, 订单方向* (标记价格- 订单价格)]}
let losePro = Math.abs(Math.min(0, lossType * (markPrice - price)));
// 订单价格: 限价单:我挂的价格 市价单:卖一
// 买单(做多),最大可用资金可用/{订单价格/杠杆倍数+绝对值(min0,标记价格-订单价格)}
// 卖单(做空),最大可用资金可用/{max(标记价,订单价格)/杠杆倍数+绝对值(min0,订单价格-标记价)}
let amount = balance / (usePrice / marginLevel + losePro);//计算开仓亏损
//扣的钱按全部购买算
amount /= (1 + serviceCharge);//计算手续费
amount /= (1 + maintMarginPro);//计算维持保证金
amount *= (1 - lastNumPro_MarketPrice);//保留点
amount = _floor(amount);
return amount;
}
/** 期货仓库里有多少个eth,type:0做多的仓,1做空的仓 */
function getCoinNum(type) {
var position = getNowPosition();
if (position.length > 0) {
let data = position[0];//默认持仓就1个
// let num = _floor(data.Amount);
let num = data.Amount;
if (data.Type == type) {
return num;
}
}
return 0;
}
/** 剩余钱不到总钱2%,不操作,避免小额交易 */
function canBuy() {
var position = getNowPosition();
if (position.length > 0) {
let acc = GetAcc();
let allMoney = getAllMoney();
if (acc.Balance / allMoney < (buyMinPro + maintMarginPro)) {
return false;
}
}
return true;
}
/** 估计当前总资产 */
function getAllMoney() {
let acc = GetAcc();
let allMoney = acc.Balance;//可用余额
allMoney += acc.FrozenBalance;//冻结余额
var position = getNowPosition();
if (position.length > 0) {
let data = position[0];//默认持仓就1个
let markPrice = getMarkPrice();
if (data.Type == 0) {//做多
allMoney += data.Amount * markPrice;
} else if (data.Type == 1) {//做空
allMoney += data.Amount * data.Price;//做空前的本金
allMoney += (data.Price - markPrice) * data.Amount;//做空的收益
}
}
return allMoney;
}
/** 获得标记价格 */
function getMarkPrice() {
return nowMarkPrice;
}
/** 获得标记价格 */
function setMarkPrice() {
//模拟回测
let ticker = _C(exchange.GetTicker);//获得行情数据最多10分钟
nowMarkPrice = ticker.Last;//最后一次交易价格
}
/** 最少买0.001个eth */
function getMinNum() {
return 0.001;
}
/** 向下取整,保留3位小数点(开仓手数:最低设置0.001个ETH) */
function _floor(num, min = 3) {
num = Math.floor(num * Math.pow(10, min)) / Math.pow(10, min);
num = parseFloat(num);//string转Float
return num;
}
/** 返回当前时间戳-秒 */
function getNowTime() {
return Unix();
}
/** 画k线图 */
function PlotMultRecords(bars, cfgName, seriesName, extension) {
var index = -1;
var eleIndex = -1;
do {
var cfgInfo = registerInfo[cfgName];
if (typeof (cfgInfo) == "undefined") {
var cfg = {
name: cfgName,
__isStock: true,
title: {
text: cfgName
},
tooltip: {
xDateFormat: '%Y-%m-%d %H:%M:%S, %A'
},
legend: {
enabled: true,
},
plotOptions: {
candlestick: {
color: '#d75442',
upColor: '#6ba583'
}
},
rangeSelector: {
buttons: [{
type: 'hour',
count: 1,
text: '1h'
}, {
type: 'hour',
count: 3,
text: '3h'
}, {
type: 'hour',
count: 8,
text: '8h'
}, {
type: 'all',
text: 'All'
}],
selected: 2,
inputEnabled: true
},
series: [{
type: 'candlestick',
name: seriesName,
id: seriesName,
data: []
}],
}
if (typeof (extension) != "undefined") {
cfg.extension = extension;
}
registerInfo[cfgName] = {
"cfgIdx": arrCfg.length,
"seriesIdxs": [{
seriesName: seriesName,
index: arrCfg.length,
type: "candlestick",
preBarTime: 0
}],
};
arrCfg.push(cfg);
updateSeriesIdx();
}
chart.update(arrCfg);//刷新所有图表
_.each(registerInfo[cfgName].seriesIdxs, function (ele, i) {
if (ele.seriesName == seriesName && ele.type == "candlestick") {
index = ele.index;
eleIndex = i;
}
});
if (index == -1) {
arrCfg[registerInfo[cfgName].cfgIdx].series.push({
type: 'candlestick',
name: seriesName,
id: seriesName,
data: []
});
registerInfo[cfgName].seriesIdxs.push({
seriesName: seriesName,
index: arrCfg.length,
type: "candlestick",
preBarTime: 0
});
updateSeriesIdx();
}
} while (index == -1)
for (var i = 0; i < bars.length; i++) {
let bar = bars[i];
let data = [bar.Time, bar.Open, bar.High, bar.Low, bar.Close];//写入图表的一个周期数据
let preBarTime = registerInfo[cfgName].seriesIdxs[eleIndex].preBarTime;
if (bar.Time == preBarTime) {
chart.add(index, data, -1);//更新当前周期
} else if (bar.Time > preBarTime) {
registerInfo[cfgName].seriesIdxs[eleIndex].preBarTime = bar.Time
chart.add(index, data);//添加历史(不变了)
}
}
}
function updateSeriesIdx() {
var index = 0
var map = {}
_.each(arrCfg, function (cfg) {
_.each(cfg.series, function (series) {
var key = cfg.name + "|" + series.name
map[key] = index
index++
})
})
for (var cfgName in registerInfo) {
_.each(arrCfg, function (cfg, cfgIdx) {
if (cfg.name == cfgName) {
registerInfo[cfgName].cfgIdx = cfgIdx
}
})
for (var i in registerInfo[cfgName].seriesIdxs) {
var seriesName = registerInfo[cfgName].seriesIdxs[i].seriesName
var key = cfgName + "|" + seriesName
if (typeof (map[key]) != "undefined") {
registerInfo[cfgName].seriesIdxs[i].index = map[key]
}
if (registerInfo[cfgName].seriesIdxs[i].type == "candlestick") {
registerInfo[cfgName].seriesIdxs[i].preBarTime = 0
} else if (registerInfo[cfgName].seriesIdxs[i].type == "line") {
registerInfo[cfgName].seriesIdxs[i].preDotTime = 0
} else if (registerInfo[cfgName].seriesIdxs[i].type == "flag") {
registerInfo[cfgName].seriesIdxs[i].preFlagTime = 0
}
}
}
if (!chart) {
chart = Chart(arrCfg)
}
chart.update(arrCfg)
chart.reset()
_G("registerInfo", registerInfo)
_G("arrCfg", arrCfg)
for (var cfgName in registerInfo) {
for (var i in registerInfo[cfgName].seriesIdxs) {
var buffer = registerInfo[cfgName].seriesIdxs[i].buffer
var index = registerInfo[cfgName].seriesIdxs[i].index
if (buffer && buffer.length != 0 && registerInfo[cfgName].seriesIdxs[i].type == "line" && registerInfo[cfgName].seriesIdxs[i].preDotTime == 0) {
_.each(buffer, function (obj) {
chart.add(index, [obj.ts, obj.dot])
registerInfo[cfgName].seriesIdxs[i].preDotTime = obj.ts
})
} else if (buffer && buffer.length != 0 && registerInfo[cfgName].seriesIdxs[i].type == "flag" && registerInfo[cfgName].seriesIdxs[i].preFlagTime == 0) {
_.each(buffer, function (obj) {
chart.add(index, obj.data)
registerInfo[cfgName].seriesIdxs[i].preFlagTime = obj.ts
})
}
}
}
}
function checkBufferLen(buffer, maxLen) {
while (buffer.length > maxLen) {
buffer.shift()
}
}
/** 画线条 */
function PlotMultLine(cfgName, seriesName, dot, ts, color, extension) {
var index = -1;
var eleIndex = -1;
do {
var cfgInfo = registerInfo[cfgName]
if (typeof (cfgInfo) == "undefined") {
var cfg = {
name: cfgName,
__isStock: true,
title: {
text: cfgName
},
xAxis: {
type: 'datetime'
},
series: [{
type: 'line',
name: seriesName,
id: seriesName,
color: color,
data: [],
}]
};
if (extension) {
cfg.extension = extension;
}
registerInfo[cfgName] = {
"cfgIdx": arrCfg.length,
"seriesIdxs": [{
seriesName: seriesName,
index: arrCfg.length,
type: "line",
color: color,
buffer: [],
preDotTime: 0
}],
};
arrCfg.push(cfg);
updateSeriesIdx();
}
chart.update(arrCfg);
_.each(registerInfo[cfgName].seriesIdxs, function (ele, i) {
if (ele.seriesName == seriesName && ele.type == "line") {
index = ele.index;
eleIndex = i;
}
})
if (index == -1) {
arrCfg[registerInfo[cfgName].cfgIdx].series.push({
type: 'line',
name: seriesName,
id: seriesName,
color: color,
data: [],
});
registerInfo[cfgName].seriesIdxs.push({
seriesName: seriesName,
index: arrCfg.length,
type: "line",
color: color,
buffer: [],
preDotTime: 0
});
updateSeriesIdx();
}
} while (index == -1)
if (typeof (ts) == "undefined") {
ts = new Date().getTime();
}
var buffer = registerInfo[cfgName].seriesIdxs[eleIndex].buffer;
if (registerInfo[cfgName].seriesIdxs[eleIndex].preDotTime != ts) {
registerInfo[cfgName].seriesIdxs[eleIndex].preDotTime = ts;
chart.add(index, [ts, dot]);
buffer.push({
ts: ts,
dot: dot
});
checkBufferLen(buffer, maxBufferLen);
} else {
chart.add(index, [ts, dot], -1);
buffer[buffer.length - 1].dot = dot;
}
}
/** 画flag */
function PlotMultFlag(cfgName, seriesName, ts, text, title, shape, color, onSeriesName) {
if (typeof (cfgName) == "undefined" || typeof (registerInfo[cfgName]) == "undefined") {
throw "need cfgName!";
}
var index = -1;
var eleIndex = -1;
do {
chart.update(arrCfg);
_.each(registerInfo[cfgName].seriesIdxs, function (ele, i) {
if (ele.seriesName == seriesName && ele.type == "flag") {
index = ele.index;
eleIndex = i;
}
});
if (index == -1) {
arrCfg[registerInfo[cfgName].cfgIdx].series.push({
type: 'flags',
name: seriesName,
onSeries: onSeriesName || arrCfg[registerInfo[cfgName].cfgIdx].series[0].id,
data: []
});
registerInfo[cfgName].seriesIdxs.push({
seriesName: seriesName,
index: arrCfg.length,
type: "flag",
buffer: [],
preFlagTime: 0
});
updateSeriesIdx();
}
} while (index == -1)
if (typeof (ts) == "undefined") {
ts = new Date().getTime();
}
var buffer = registerInfo[cfgName].seriesIdxs[eleIndex].buffer;
var obj = {
x: ts,
color: color,
shape: shape,
title: title,
text: text
};
if (registerInfo[cfgName].seriesIdxs[eleIndex].preFlagTime != ts) {
registerInfo[cfgName].seriesIdxs[eleIndex].preFlagTime = ts;
chart.add(index, obj);
buffer.push({
ts: ts,
data: obj
});
checkBufferLen(buffer, maxBufferLen);
} else {
chart.add(index, obj, -1);
buffer[buffer.length - 1].data = obj;
}
}
function testLog(txt) {
Log(txt);
}
function getNowPosition() {
return nowPosition;
}
function setNowPosition() {
nowPosition = _C(exchange.GetPosition);
}
/** 把时间秒改成天\小时等 */
function getTimeTxt(time) {
if (time < 60) {
var timeTxt = time;
var key = '秒';
} else if (time < 3600) {
timeTxt = time / 60;
key = '分钟';
} else if (time < 3600 * 24) {
timeTxt = time / 3600;
key = '小时';
} else {
timeTxt = time / 3600 / 24;
key = '天';
}
timeTxt = Math.floor(timeTxt * 10) / 10 + key;//保留1位小数点
return timeTxt;
}
Detail
https://www.fmz.com/strategy/424959
Last Modified
2023-08-26 14:36:31