863 lines
26 KiB
Markdown
863 lines
26 KiB
Markdown
|
||||
|
|
> Name
|
|||
|
|
|
|||
|
|
价格振幅均线-3年33万倍收益
|
|||
|
|
|
|||
|
|
> Author
|
|||
|
|
|
|||
|
|
xiaode123
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
> Source (javascript)
|
|||
|
|
|
|||
|
|
``` 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/405725
|
|||
|
|
|
|||
|
|
> Last Modified
|
|||
|
|
|
|||
|
|
2023-04-19 15:04:59
|