update
This commit is contained in:
@@ -4,19 +4,22 @@
|
||||
> 开源策略列表
|
||||
|
||||
|
||||
- [商品期货跨期对冲](https://www.botvs.com/strategy/23896)
|
||||
- [jLib借贷函数库](https://www.botvs.com/strategy/23943)
|
||||
- [全球10大交易系统之 Aberration 多品种商品期货交易系统](https://www.botvs.com/strategy/25943)
|
||||
- [商品期货交易类库](https://www.botvs.com/strategy/12961)
|
||||
- [获取和讯期货数据(Python版本)](https://www.botvs.com/strategy/26017)
|
||||
- [高频收割机一号](https://www.botvs.com/strategy/6237)
|
||||
- [高精度回测](https://www.botvs.com/strategy/11575)
|
||||
- [商品期货跨期对冲 - 百行代码实现](https://www.botvs.com/strategy/23896)
|
||||
- [jLibPub_借贷函数库](https://www.botvs.com/strategy/23943)
|
||||
- [R-Breaker 交易策略](https://www.botvs.com/strategy/23531)
|
||||
- [多平台对冲稳定套利 V2.1 (复制)](https://www.botvs.com/strategy/23718)
|
||||
- [从第三方网站获取K线 (0804更新)+ bitstamp+okcoin现货](https://www.botvs.com/strategy/21487)
|
||||
- [CTP商品期货多品种海龟交易策略](https://www.botvs.com/strategy/17289)
|
||||
- [商品期货交易类库](https://www.botvs.com/strategy/12961)
|
||||
- [(13)策略框架](https://www.botvs.com/strategy/20663)
|
||||
- [python版现货数字货币交易类库](https://www.botvs.com/strategy/21104)
|
||||
- [Dual Thrust_双轨突破_策略1](https://www.botvs.com/strategy/22983)
|
||||
- [Python 机器学习之 SVM 预测买卖](https://www.botvs.com/strategy/21370)
|
||||
- [震荡突破策略 (删减B值触发平仓、动态调整触发值 优化ing)](https://www.botvs.com/strategy/22759)
|
||||
- [顺势_高频_交易_测试3](https://www.botvs.com/strategy/22183)
|
||||
- [测试CTP商品期货连接状态 (图表显示)](https://www.botvs.com/strategy/22838)
|
||||
- [Python API 测试](https://www.botvs.com/strategy/21365)
|
||||
- [均线策略(python版)](https://www.botvs.com/strategy/21157)
|
||||
- [顺势_高频_交易_测试2——失败](https://www.botvs.com/strategy/21938)
|
||||
@@ -53,7 +56,6 @@
|
||||
- [窜天猴 - 移动止损版](https://www.botvs.com/strategy/13594)
|
||||
- [窜天猴 多空较量 爆破版](https://www.botvs.com/strategy/13597)
|
||||
- [破冰者](https://www.botvs.com/strategy/9929)
|
||||
- [高精度回测](https://www.botvs.com/strategy/11575)
|
||||
- [函数自动容错模板](https://www.botvs.com/strategy/11609)
|
||||
- [Dual Thrust OKCoin 期货](https://www.botvs.com/strategy/12101)
|
||||
- [RSI_now_sb_ok](https://www.botvs.com/strategy/11604)
|
||||
@@ -69,6 +71,7 @@
|
||||
- [均线买卖策略](https://www.botvs.com/strategy/143)
|
||||
- [趋势跟踪震荡策略](https://www.botvs.com/strategy/179)
|
||||
- [多平台账户统计](https://www.botvs.com/strategy/7827)
|
||||
- [差价监控](https://www.botvs.com/strategy/7944)
|
||||
- [计划委托买入](https://www.botvs.com/strategy/638)
|
||||
- [计划委托卖出](https://www.botvs.com/strategy/747)
|
||||
- [股票证券查询买卖示例](https://www.botvs.com/strategy/5541)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/23943
|
||||
策略名称: jLib借贷函数库
|
||||
策略名称: jLibPub_借贷函数库
|
||||
策略作者: J
|
||||
策略描述:
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/21487
|
||||
策略名称: 从第三方网站获取K线 (0804更新)+ bitstamp+okcoin现货
|
||||
策略作者: 青铜战国级机器人
|
||||
策略描述:
|
||||
|
||||
对于不支持获取K线数据的平台(BitVC期货、BTCC的BTC现货、中国比特币的ETH、ETC),如果在策略开始时必须获取足够多的K线,使用本模板则可从第三方网站直接获得平台的历史K线数据。
|
||||
注意:
|
||||
K线数据每3秒更新一次,因此无法高频调用。
|
||||
仅适用于实盘交易。
|
||||
作者不保证第三方数据准确性和程序正确性,仅供学习参考。
|
||||
|
||||
0427更新:处理了Parse JSON数据时可能抛出的异常、出现异常时返回值统一为null。
|
||||
|
||||
*/
|
||||
|
||||
|
||||
$.AltRecords = function(exchange, timeframe, size, includeLastBar) {
|
||||
var symbol;
|
||||
var info;
|
||||
var record = [];
|
||||
if (!size) size="";
|
||||
// 目前只支持以下三个交易所,其余交易所接口可参考https://www.btc123.com/api
|
||||
if (exchange.GetName().indexOf('Futures_BitVC') != -1) {
|
||||
symbol = "bitvcbtccnyfuture";
|
||||
}
|
||||
else if (exchange.GetName().indexOf('BTCC') != -1 && exchange.GetCurrency().indexOf('BTC') != -1) {
|
||||
symbol = "btcchinabtccny";
|
||||
}
|
||||
else if (exchange.GetName().indexOf('CHBTC') != -1 && exchange.GetCurrency().indexOf('ETH') != -1) {
|
||||
symbol = "chbtcethcny";
|
||||
}
|
||||
else if (exchange.GetName().indexOf('CHBTC') != -1 && exchange.GetCurrency().indexOf('ETC') != -1) {
|
||||
symbol = "chbtcetccny";
|
||||
}
|
||||
else if (exchange.GetName().indexOf('CHBTC') != -1 && exchange.GetCurrency().indexOf('BTC') != -1) {
|
||||
symbol = "chbtcbtccny";
|
||||
}
|
||||
else if (exchange.GetName().indexOf('Bitstamp') != -1 && exchange.GetCurrency().indexOf('BTC') != -1) {
|
||||
symbol = "bitstampbtcusd";
|
||||
}
|
||||
else if (exchange.GetName().indexOf('Futures_OKCoin') != -1 && exchange.GetCurrency().indexOf('BTC') != -1) {
|
||||
symbol = "okcoinbtcusdfuture";
|
||||
}
|
||||
else if (exchange.GetName().indexOf('OKCoin_EN') != -1 && exchange.GetCurrency().indexOf('BTC') != -1) {
|
||||
symbol = "okcoinbtcusd";
|
||||
}
|
||||
else if (exchange.GetName().indexOf('OKCoin') != -1 && exchange.GetCurrency().indexOf('BTC') != -1) {
|
||||
symbol = "okcoincnbtccny";
|
||||
}
|
||||
else if (exchange.GetName().indexOf('Huobi') != -1 && exchange.GetCurrency().indexOf('BTC') != -1) {
|
||||
symbol = "huobibtccny";
|
||||
}
|
||||
else if (exchange.GetName().indexOf('BTCE') != -1 && exchange.GetCurrency().indexOf('BTC') != -1) {
|
||||
symbol = "btcebtcusd";
|
||||
}
|
||||
|
||||
if (symbol) {
|
||||
try {
|
||||
info = JSON.parse(HttpQuery('https://www.btc123.com/market/kline?symbol='+symbol+'&type='+timeframe+'&size='+(includeLastBar ? size : size+1)));
|
||||
if (info && info.isSuc) {
|
||||
info = JSON.parse(info.datas.data);
|
||||
}
|
||||
else {
|
||||
Log("获取K线时发生错误:", info && info.des ? info.des : "网络错误");
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
Log("获取K线时发生错误:", info && info.des ? info.des : "网络错误");
|
||||
return null;
|
||||
}
|
||||
for (var i = 0; i < (includeLastBar ? info.length : info.length-1); i++) {
|
||||
record.push({"Time": info[i][0], "Open": info[i][1], "High": info[i][2], "Low": info[i][3], "Close": info[i][4], "Volume": info[i][5]});
|
||||
}
|
||||
return record;
|
||||
}
|
||||
return exchange.GetRecords(); // 不支持的交易所采用默认方式处理(忽略所有参数,如时间周期、长度等)。
|
||||
};
|
||||
|
||||
function main() {
|
||||
Log(exchange.GetName());
|
||||
var rec = $.AltRecords(exchange, "5min", 100); // 获取5分钟K线, 100条, 不含最后一条Bar
|
||||
if (rec) Log(rec.length, rec[rec.length-1]);
|
||||
rec = $.AltRecords(exchange, "1hour", 1000000); // 获取4小时K线, 100条, 含最后一条Bar
|
||||
Log(rec);
|
||||
if (rec) Log(rec.length, rec[rec.length-1]);
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/25943
|
||||
策略名称: 全球10大交易系统之 Aberration 多品种商品期货交易系统
|
||||
策略作者: Zero
|
||||
策略描述:
|
||||
|
||||
全球10大交易系统之一, 代码对细节处理很完善, 整体策略框架成熟稳定而且复用性强,逻辑清晰, 是做为商品期货多品种操作策略入门的不二之选
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
------------ ---------------------- ----------
|
||||
Symbols MA701:5,CF701:3,rb1701 合约品种
|
||||
NPeriod 35 计算周期
|
||||
Ks 2 上轨系数
|
||||
Kx 2 下轨系数
|
||||
AmountOP true 默认开仓合约张数
|
||||
LoopInterval 3 轮询间隔(秒)
|
||||
NotifyWX true 下单微信通知
|
||||
Reset false 启动时重置
|
||||
RCMode true 启用风控模块
|
||||
MinNetAsset 10000 最小净值
|
||||
RCCoverAll true 触发后平掉所有仓位
|
||||
AutoRestore false 自动根据仓位恢复进度
|
||||
|
||||
按钮 默认值 描述
|
||||
----- -------------------------- ----
|
||||
Debug Log(exchange.GetAccount()) 调试
|
||||
*/
|
||||
|
||||
function Aberration(q, e, symbol, period, upRatio, downRatio, opAmount) {
|
||||
var self = {}
|
||||
self.q = q
|
||||
self.e = e
|
||||
self.symbol = symbol
|
||||
self.upTrack = 0
|
||||
self.middleTrack = 0
|
||||
self.downTrack = 0
|
||||
self.nPeriod = period
|
||||
self.upRatio = upRatio
|
||||
self.downRatio = downRatio
|
||||
self.opAmount = opAmount
|
||||
self.marketPosition = 0
|
||||
|
||||
self.lastErrMsg = ''
|
||||
self.lastErrTime = ''
|
||||
self.lastBar = {
|
||||
Time: 0,
|
||||
Open: 0,
|
||||
High: 0,
|
||||
Low: 0,
|
||||
Close: 0,
|
||||
Volume: 0
|
||||
}
|
||||
self.symbolDetail = null
|
||||
self.lastBarTime = 0
|
||||
self.tradeCount = 0
|
||||
self.isBusy = false
|
||||
|
||||
self.setLastError = function(errMsg) {
|
||||
self.lastErrMsg = errMsg
|
||||
self.lastErrTime = errMsg.length > 0 ? _D() : ''
|
||||
}
|
||||
|
||||
self.getStatus = function() {
|
||||
return [self.symbol, self.opAmount, self.upTrack, self.downTrack, self.middleTrack, _N(self.lastBar.Close), (self.marketPosition == 0 ? "--" : (self.marketPosition > 0 ? "多#ff0000" : "空#0000ff")), self.tradeCount, self.lastErrMsg, self.lastErrTime]
|
||||
}
|
||||
self.getMarket = function() {
|
||||
return [self.symbol, _D(self.lastBarTime), _N(self.lastBar.Open), _N(self.lastBar.High), _N(self.lastBar.Low), _N(self.lastBar.Close), self.lastBar.Volume]
|
||||
}
|
||||
|
||||
self.restore = function(positions) {
|
||||
for (var i = 0; i < positions.length; i++) {
|
||||
if (positions[i].ContractType == self.symbol) {
|
||||
self.marketPosition += positions[i].Amount * ((positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) ? 1 : -1)
|
||||
}
|
||||
}
|
||||
if (self.marketPosition !== 0) {
|
||||
self.tradeCount++
|
||||
Log("恢复", self.symbol, "当前持仓为", self.marketPosition)
|
||||
}
|
||||
}
|
||||
|
||||
self.poll = function() {
|
||||
if (self.isBusy) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!$.IsTrading(self.symbol)) {
|
||||
self.setLastError("不在交易时间内")
|
||||
return false
|
||||
}
|
||||
|
||||
if (!self.e.IO("status")) {
|
||||
self.setLastError("未连接交易所")
|
||||
return false
|
||||
}
|
||||
|
||||
var detail = self.e.SetContractType(self.symbol)
|
||||
if (!detail) {
|
||||
self.setLastError("切换合约失败")
|
||||
return false
|
||||
}
|
||||
if (!self.symbolDetail) {
|
||||
self.symbolDetail = detail
|
||||
Log("合约", detail.InstrumentName.replace(/\s+/g, ""), ", 策略一次开仓:", self.opAmount, "手, 一手", detail.VolumeMultiple, "份, 最大下单量", detail.MaxLimitOrderVolume, "保证金率:", detail.LongMarginRatio.toFixed(4), detail.ShortMarginRatio.toFixed(4), "交割日期", detail.StartDelivDate);
|
||||
}
|
||||
var records = self.e.GetRecords()
|
||||
if (!records || records.length == 0) {
|
||||
self.setLastError("获取柱线失败")
|
||||
return false
|
||||
}
|
||||
|
||||
var bar = records[records.length - 1]
|
||||
self.lastBar = bar
|
||||
|
||||
if (records.length <= self.nPeriod) {
|
||||
self.setLastError("柱线长度不够")
|
||||
return false
|
||||
}
|
||||
|
||||
if (self.lastBarTime < bar.Time) {
|
||||
var sum = 0
|
||||
var pos = records.length - self.nPeriod - 1
|
||||
for (var i = pos; i < records.length - 1; i++) {
|
||||
sum += records[i].Close
|
||||
}
|
||||
var avg = sum / self.nPeriod
|
||||
var std = 0
|
||||
for (i = pos; i < records.length - 1; i++) {
|
||||
std += Math.pow(records[i].Close - avg, 2)
|
||||
}
|
||||
std = Math.sqrt(std / self.nPeriod)
|
||||
|
||||
self.upTrack = _N(avg + (self.upRatio * std))
|
||||
self.downTrack = _N(avg - (self.downRatio * std))
|
||||
self.middleTrack = _N(avg)
|
||||
self.lastBarTime = bar.Time
|
||||
}
|
||||
var msg
|
||||
var act = ""
|
||||
if (self.marketPosition == 0) {
|
||||
if (bar.Close > self.upTrack) {
|
||||
msg = '做多 触发价: ' + bar.Close + ' 上轨:' + self.upTrack;
|
||||
act = "buy"
|
||||
} else if (bar.Close < self.downTrack) {
|
||||
msg = '做空 触发价: ' + bar.Close + ' 下轨:' + self.downTrack;
|
||||
act = "sell"
|
||||
}
|
||||
} else {
|
||||
if (self.marketPosition < 0 && bar.Close > self.middleTrack) {
|
||||
msg = '平空 触发价: ' + bar.Close + ' 平仓线:' + self.middleTrack;
|
||||
act = "closesell"
|
||||
} else if (self.marketPosition > 0 && bar.Close < self.middleTrack) {
|
||||
msg = '平多 触发价: ' + bar.Close + ' 平仓线:' + self.middleTrack;
|
||||
act = "closebuy"
|
||||
}
|
||||
}
|
||||
|
||||
if (act == "") {
|
||||
return true
|
||||
}
|
||||
|
||||
Log(self.symbol + ', ' + msg + (NotifyWX ? '@' : ''))
|
||||
|
||||
self.isBusy = true
|
||||
self.tradeCount += 1
|
||||
if (self.lastErrMsg != '') {
|
||||
self.setLastError('')
|
||||
}
|
||||
self.q.pushTask(self.e, self.symbol, act, self.opAmount, function(task, ret) {
|
||||
self.isBusy = false
|
||||
if (!ret) {
|
||||
return
|
||||
}
|
||||
if (task.action == "buy") {
|
||||
self.marketPosition = 1
|
||||
} else if (task.action == "sell") {
|
||||
self.marketPosition = -1
|
||||
} else {
|
||||
self.marketPosition = 0
|
||||
}
|
||||
})
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (exchange.GetName() !== 'Futures_CTP') {
|
||||
throw "只支持传统商品期货(CTP)"
|
||||
}
|
||||
|
||||
SetErrorFilter("login|ready|初始化")
|
||||
|
||||
LogStatus("Ready...")
|
||||
if (Reset) {
|
||||
LogProfitReset()
|
||||
LogReset()
|
||||
}
|
||||
|
||||
// Ref: https://www.botvs.com/bbs-topic/362
|
||||
if (typeof(exchange.IO("mode", 0)) == 'number') {
|
||||
Log("切换行情模式成功")
|
||||
}
|
||||
|
||||
LogStatus("等待与期货商服务器连接..")
|
||||
while (!exchange.IO("status")) {
|
||||
Sleep(500)
|
||||
}
|
||||
LogStatus("获取资产信息")
|
||||
var tblRuntime = {
|
||||
type: 'table',
|
||||
title: '交易信息',
|
||||
cols: ['品种', '每次开仓量', '上轨', '下轨', '中轨', '最后成交价', '仓位', '交易次数', '最后错误', '错误时间'],
|
||||
rows: []
|
||||
};
|
||||
var tblMarket = {
|
||||
type: 'table',
|
||||
title: '行情信息',
|
||||
cols: ['品种', '当前周期', '开盘', '最高', '最低', '最后成交价', '成交量'],
|
||||
rows: []
|
||||
};
|
||||
var tblPosition = {
|
||||
type: 'table',
|
||||
title: '持仓信息',
|
||||
cols: ['品种', '杠杆', '方向', '均价', '数量', '持仓盈亏'],
|
||||
rows: []
|
||||
};
|
||||
var positions = _C(exchange.GetPosition)
|
||||
if (positions.length > 0 && !AutoRestore) {
|
||||
throw "程序启动时不能有持仓, 但您可以勾选自动恢复来进行自动识别 !"
|
||||
}
|
||||
var initAccount = _C(exchange.GetAccount)
|
||||
var detail = JSON.parse(exchange.GetRawJSON())
|
||||
if (positions.length > 0) {
|
||||
initAccount.Balance += detail['CurrMargin']
|
||||
}
|
||||
var initNetAsset = detail['CurrMargin'] + detail['Available']
|
||||
var initAccountTbl = $.AccountToTable(exchange.GetRawJSON(), "初始资金")
|
||||
|
||||
if (initAccountTbl.rows.length == 0) {
|
||||
initAccountTbl.rows = [
|
||||
['Balance', '可用保证金', initAccount.Balance],
|
||||
['FrozenBalance', '冻结资金', initAccount.FrozenBalance]
|
||||
]
|
||||
}
|
||||
|
||||
var nowAcccount = initAccount
|
||||
var nowAcccountTbl = initAccountTbl
|
||||
|
||||
var symbols = Symbols.replace(/\s+/g, "").split(',')
|
||||
var pollers = []
|
||||
var prePosUpdate = 0
|
||||
var suffix = ""
|
||||
var needUpdate = false
|
||||
var holdProfit = 0
|
||||
|
||||
function updateAccount(acc) {
|
||||
nowAcccount = acc
|
||||
nowAcccountTbl = $.AccountToTable(exchange.GetRawJSON(), "当前资金")
|
||||
if (nowAcccountTbl.rows.length == 0) {
|
||||
nowAcccountTbl.rows = [
|
||||
['Balance', '可用保证金', nowAcccount.Balance],
|
||||
['FrozenBalance', '冻结资金', nowAcccount.FrozenBalance]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
var q = $.NewTaskQueue(function(task, ret) {
|
||||
needUpdate = true
|
||||
Log(task.desc, ret ? "成功" : "失败")
|
||||
var account = task.e.GetAccount()
|
||||
if (account) {
|
||||
updateAccount(account)
|
||||
}
|
||||
})
|
||||
|
||||
_.each(symbols, function(symbol) {
|
||||
var pair = symbol.split(':')
|
||||
pollers.push(Aberration(q, exchange, pair[0], NPeriod, Ks, Kx, (pair.length == 1 ? AmountOP : parseInt(pair[1]))))
|
||||
})
|
||||
|
||||
if (positions.length > 0 && AutoRestore) {
|
||||
_.each(pollers, function(poll) {
|
||||
poll.restore(positions)
|
||||
})
|
||||
}
|
||||
var isFirst = true
|
||||
while (true) {
|
||||
var cmd = GetCommand()
|
||||
if (cmd) {
|
||||
var js = cmd.split(':', 2)[1]
|
||||
Log("执行调试代码:", js)
|
||||
try {
|
||||
eval(js)
|
||||
} catch (e) {
|
||||
Log("Exception", e)
|
||||
}
|
||||
}
|
||||
tblRuntime.rows = []
|
||||
tblMarket.rows = []
|
||||
var marketAlive = false
|
||||
_.each(pollers, function(poll) {
|
||||
if (poll.poll()) {
|
||||
marketAlive = true
|
||||
}
|
||||
tblRuntime.rows.push(poll.getStatus())
|
||||
tblMarket.rows.push(poll.getMarket())
|
||||
})
|
||||
q.poll()
|
||||
Sleep(LoopInterval * 1000)
|
||||
if ((!exchange.IO("status")) || (!marketAlive)) {
|
||||
if (isFirst) {
|
||||
LogStatus("正在等待开盘...", _D())
|
||||
}
|
||||
continue
|
||||
}
|
||||
isFirst = false
|
||||
var now = new Date().getTime()
|
||||
if (marketAlive && (now - prePosUpdate > 30000 || needUpdate)) {
|
||||
var pos = exchange.GetPosition()
|
||||
if (pos) {
|
||||
holdProfit = 0
|
||||
prePosUpdate = now
|
||||
tblPosition.rows = []
|
||||
for (var i = 0; i < pos.length; i++) {
|
||||
tblPosition.rows.push([pos[i].ContractType, pos[i].MarginLevel, ((pos[i].Type == PD_LONG || pos[i].Type == PD_LONG_YD) ? '多#ff0000' : '空#0000ff'), pos[i].Price, pos[i].Amount, _N(pos[i].Profit)])
|
||||
holdProfit += pos[i].Profit
|
||||
}
|
||||
if (pos.length == 0 && needUpdate) {
|
||||
LogProfit(_N(nowAcccount.Balance - initAccount.Balance, 4), nowAcccount)
|
||||
}
|
||||
}
|
||||
needUpdate = false
|
||||
if (RCMode) {
|
||||
var account = exchange.GetAccount()
|
||||
if (account) {
|
||||
updateAccount(account)
|
||||
var detail = JSON.parse(exchange.GetRawJSON())
|
||||
var netAsset = detail['PositionProfit'] + detail['CurrMargin'] + detail['Available']
|
||||
var risk = detail['CurrMargin'] / (detail['CurrMargin'] + detail['Available'] + detail['PositionProfit'])
|
||||
suffix = ", 账户初始净值约: " + _N(initNetAsset, 2) + " , 风控最小净值要求" + MinNetAsset + " , 当前账户净值约: " + _N(netAsset, 2) + ", 盈亏约: " + _N(netAsset - initNetAsset, 3) + " 元, 风险: " + ((risk * 100).toFixed(3)) + "% #ff0000"
|
||||
if (netAsset < MinNetAsset) {
|
||||
Log("风控模块触发, 中止运行并平掉所有仓位, 当前净值约 ", netAsset, ", 要求低于最小净值:", MinNetAsset)
|
||||
if (RCCoverAll) {
|
||||
Log("开始平掉所有仓位")
|
||||
$.NewPositionManager().CoverAll()
|
||||
}
|
||||
throw "中止运行"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LogStatus('`' + JSON.stringify([tblRuntime, tblPosition, tblMarket, initAccountTbl, nowAcccountTbl]) + '`\n价格最后更新: ' + _D() + ', 持仓最后更新: ' + _D(prePosUpdate) + '\n当前持仓总盈亏: ' + _N(holdProfit, 3) + suffix)
|
||||
}
|
||||
}
|
||||
+671
-638
File diff suppressed because it is too large
Load Diff
@@ -1,25 +1,27 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/23896
|
||||
策略名称: 商品期货跨期对冲
|
||||
策略名称: 商品期货跨期对冲 - 百行代码实现
|
||||
策略作者: Zero
|
||||
策略描述:
|
||||
|
||||
简单的跨期对冲, 抛砖引玉, 细节还需要处理
|
||||
简单的跨期对冲, 抛砖引玉, 细节还需要处理, 轮训间隔可以缩小到1秒或者删除Sleep那行策略就可以随时响应价格变化
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
------------ ----- -------
|
||||
HedgeSpread 30 开仓价差
|
||||
CoverSpread 10 平仓价差
|
||||
LoopInterval true 轮询间隔(秒)
|
||||
------------ ----- ---------
|
||||
SA MA701 合约A
|
||||
SB MA705 合约B
|
||||
HedgeSpread 30 开仓价差
|
||||
CoverSpread 10 平仓价差
|
||||
LoopInterval 6 轮询间隔(秒)
|
||||
CoverAll false 启动时平掉所有仓位
|
||||
*/
|
||||
|
||||
|
||||
function Hedge(q, e, symbolA, symbolB, hedgeSpread, coverSpread) {
|
||||
function Hedge(q, e, initAccount, symbolA, symbolB, hedgeSpread, coverSpread) {
|
||||
var self = {}
|
||||
self.q = q
|
||||
self.initAccount = initAccount
|
||||
self.status = 0
|
||||
self.symbolA = symbolA
|
||||
self.symbolB = symbolB
|
||||
@@ -48,7 +50,8 @@ function Hedge(q, e, symbolA, symbolB, hedgeSpread, coverSpread) {
|
||||
if (!tickerB) {
|
||||
return
|
||||
}
|
||||
LogStatus("A卖B买", _N(tickerA.Buy - tickerB.Sell), "A买B卖", _N(tickerA.Sell-tickerB.Buy))
|
||||
// _D()是内置时间格式化函数, 不传参就是取当前时间, 可以传时间戳
|
||||
LogStatus(_D(), "A卖B买", _N(tickerA.Buy - tickerB.Sell), "A买B卖", _N(tickerA.Sell-tickerB.Buy))
|
||||
var action = 0; // 1: A sell B buy 2: A buy B sell
|
||||
|
||||
if (self.status == 0) {
|
||||
@@ -89,6 +92,8 @@ function Hedge(q, e, symbolA, symbolB, hedgeSpread, coverSpread) {
|
||||
self.status = 1
|
||||
} else {
|
||||
self.status = 0
|
||||
var account = _C(exchange.GetAccount)
|
||||
LogProfit(account.Balance - self.initAccount.Balance, account)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -98,17 +103,22 @@ function Hedge(q, e, symbolA, symbolB, hedgeSpread, coverSpread) {
|
||||
|
||||
|
||||
function main() {
|
||||
SetErrorFilter("ready|login|timeout")
|
||||
Log("正在与交易服务器连接...")
|
||||
while (!exchange.IO("status"))Sleep(1000);
|
||||
Log("与交易服务器连接成功")
|
||||
var initAccount = _C(exchange.GetAccount)
|
||||
Log(initAccount)
|
||||
var n = 0
|
||||
var q = $.NewTaskQueue(function(task, ret) {
|
||||
Log(task.desc, ret ? "成功" : "失败")
|
||||
n++
|
||||
if (n % 4 == 0) {
|
||||
var account = _C(exchange.GetAccount)
|
||||
LogProfit(account.Balance - initAccount.Balance, account)
|
||||
}
|
||||
})
|
||||
var t = Hedge(q, exchanges[0], SA, SB, HedgeSpread, CoverSpread)
|
||||
if (CoverAll) {
|
||||
Log("开始平掉所有残余仓位...");
|
||||
$.NewPositionManager().CoverAll();
|
||||
Log("操作完成");
|
||||
}
|
||||
var t = Hedge(q, exchanges[0], initAccount, SA, SB, HedgeSpread, CoverSpread)
|
||||
while (true) {
|
||||
q.poll()
|
||||
t.poll()
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/1340
|
||||
策略出处: https://www.botvs.com/strategy/7944
|
||||
策略名称: 差价监控
|
||||
策略作者: Zero
|
||||
策略作者: 折腾中
|
||||
策略描述:
|
||||
|
||||
只支持两个交易所, 可自定义差价的类型, 支持2.77托管者的自定义图表功能
|
||||
@@ -17,10 +17,6 @@ EnableCR false 自定义汇率
|
||||
USDCNY false USDCNY
|
||||
NormalDiff 0.1 普通差价
|
||||
HighDiff 0.3 较高差价
|
||||
|
||||
按钮 默认值 描述
|
||||
---- ---------- ----
|
||||
重置数据 __button__ @
|
||||
*/
|
||||
|
||||
|
||||
@@ -28,51 +24,6 @@ var __lastDiff = 0;
|
||||
var __AType = ["Last", "Buy", "Sell"][AType];
|
||||
var __BType = ["Last", "Buy", "Sell"][BType];
|
||||
|
||||
var cfg = {
|
||||
tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},
|
||||
title : { text : '差价分析图'},
|
||||
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: 0,
|
||||
inputEnabled: false
|
||||
},
|
||||
xAxis: { type: 'datetime'},
|
||||
yAxis : {
|
||||
plotLines : [{
|
||||
value : 0.0,
|
||||
color : 'black',
|
||||
dashStyle : 'shortdash',
|
||||
width : 3,
|
||||
}, {
|
||||
value : NormalDiff,
|
||||
color : 'green',
|
||||
dashStyle : 'shortdash',
|
||||
width : 1,
|
||||
}, {
|
||||
value : HighDiff,
|
||||
color : 'red',
|
||||
dashStyle : 'shortdash',
|
||||
width : 1,
|
||||
},{
|
||||
value : -NormalDiff,
|
||||
color : 'green',
|
||||
dashStyle : 'shortdash',
|
||||
width : 1,
|
||||
}, {
|
||||
value : -HighDiff,
|
||||
color : 'red',
|
||||
dashStyle : 'shortdash',
|
||||
width : 1,
|
||||
}]
|
||||
},
|
||||
series : [{
|
||||
name : '价差',
|
||||
data : [],
|
||||
tooltip: {
|
||||
valueDecimals: 2
|
||||
}
|
||||
}]
|
||||
};
|
||||
function _N(v, precision) {
|
||||
if (typeof(precision) != 'number') {
|
||||
precision = 4;
|
||||
@@ -110,9 +61,6 @@ function onTick() {
|
||||
}
|
||||
if (diff != __lastDiff) {
|
||||
// add添加数据到series, 参数格式为[series序号, 数据];
|
||||
cfg.yAxis.plotLines[0].value=diff;
|
||||
cfg.subtitle={text:'当前价差:' + diff};
|
||||
__chart.update(cfg);
|
||||
__chart.add([0, [new Date().getTime(), diff]]);
|
||||
__lastDiff = diff;
|
||||
}
|
||||
@@ -126,7 +74,46 @@ function main() {
|
||||
throw "只支持两个交易所对冲";
|
||||
}
|
||||
// 传给Chart函数的必须是一个与上下文无关的结构体(附合HighStocks规则, 详情参数HighStocks使用方法)
|
||||
__chart = Chart(cfg);
|
||||
__chart = Chart({
|
||||
tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},
|
||||
title : { text : '差价分析图'},
|
||||
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: 0,
|
||||
inputEnabled: false
|
||||
},
|
||||
xAxis: { type: 'datetime'},
|
||||
yAxis : {
|
||||
plotLines : [{
|
||||
value : NormalDiff,
|
||||
color : 'green',
|
||||
dashStyle : 'shortdash',
|
||||
width : 1,
|
||||
}, {
|
||||
value : HighDiff,
|
||||
color : 'red',
|
||||
dashStyle : 'shortdash',
|
||||
width : 1,
|
||||
},{
|
||||
value : -NormalDiff,
|
||||
color : 'green',
|
||||
dashStyle : 'shortdash',
|
||||
width : 1,
|
||||
}, {
|
||||
value : -HighDiff,
|
||||
color : 'red',
|
||||
dashStyle : 'shortdash',
|
||||
width : 1,
|
||||
}]
|
||||
},
|
||||
series : [{
|
||||
name : '价差',
|
||||
data : [],
|
||||
tooltip: {
|
||||
valueDecimals: 2
|
||||
}
|
||||
}]
|
||||
});
|
||||
// reset 清空所有图表之前的信息
|
||||
// __chart.reset();
|
||||
if (EnableCR) {
|
||||
@@ -134,7 +121,7 @@ function main() {
|
||||
var rate = exchanges[i].GetRate();
|
||||
if (rate != 1) {
|
||||
exchanges[i].SetRate(USDCNY);
|
||||
Log("更改", exchanges[i].GetName(), "汇率", rate, "为", USDCNY);
|
||||
Log("更改", exchanges[i].GetName(), "汇率", rate, "为", USDCNY)
|
||||
}
|
||||
var eName = exchanges[i].GetName();
|
||||
if (eName == "Futures_BitVC") {
|
||||
@@ -148,11 +135,5 @@ function main() {
|
||||
while (true) {
|
||||
onTick();
|
||||
Sleep(TickInterval);
|
||||
if (GetCommand() === '重置数据') {
|
||||
LogReset();
|
||||
LogProfitReset();
|
||||
__chart.reset();
|
||||
Log("数据重置成功");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/22838
|
||||
策略名称: 测试CTP商品期货连接状态 (图表显示)
|
||||
策略作者: 小小梦
|
||||
策略描述:
|
||||
|
||||
测试CTP商品期货连接状态
|
||||
1、图标显示
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
---------------- ----- -------
|
||||
testContractType MA701 测试的合约类型
|
||||
*/
|
||||
|
||||
//获取多个品种的信息 SR', 'CF', 'RM', 'MA', 'PTA', 'ZC', 'FG', 'IO
|
||||
|
||||
var ChartCfg = {
|
||||
__isStock: true,
|
||||
title: {
|
||||
text: 'CTP商品期货 交易所连接状态记录'
|
||||
},
|
||||
xAxis:{type: 'datetime'},
|
||||
yAxis: {
|
||||
//tickInterval: 1,
|
||||
//tickAmount: 3,
|
||||
max: 1.5,
|
||||
min: -1.5,
|
||||
plotLines: [{
|
||||
value: 0,
|
||||
color: 'red',
|
||||
width: 5,
|
||||
label: {
|
||||
text: '已连接',
|
||||
align: 'center'
|
||||
},
|
||||
}, {
|
||||
value: 0,
|
||||
color: 'green',
|
||||
width: 5,
|
||||
label: {
|
||||
text: '未连接',
|
||||
align: 'center'
|
||||
},
|
||||
}]
|
||||
},
|
||||
series: [{
|
||||
type: 'line',
|
||||
name: '连接状态',
|
||||
id: 'state',
|
||||
data: []
|
||||
}, {
|
||||
type: 'flags',
|
||||
onSeries: 'state',
|
||||
data: [],
|
||||
}]
|
||||
};
|
||||
|
||||
|
||||
|
||||
var ContractArray = ['SR701', 'CF701', 'RM701', 'MA701', 'ZC701', 'FG701']; //
|
||||
function GetSomeContractMarketInfo(ContractArray){
|
||||
var array = []; //储存获取的信息
|
||||
_.each(ContractArray,function(ContractType){
|
||||
exchange.SetContractType(ContractType);
|
||||
array.push({TypeName : ContractType ,ticker : exchange.GetTicker()});
|
||||
});
|
||||
|
||||
for(var i = 0 ; i < array.length; i++){
|
||||
Log(array[i]);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
var table = null;
|
||||
function main() {
|
||||
Log("测试CTP商品期货连接状态");
|
||||
SetErrorFilter("login|ready");
|
||||
//初始化图表
|
||||
table = $.TableInit(3,6);
|
||||
table.a0 = "实时状态:exchange.IO('status') ↓";
|
||||
table.b0 = "test ContractType ↓";
|
||||
table.c0 = "infomation ↓";
|
||||
table.b2 = "ticker ↓#FF0000";
|
||||
table.b4 = "records[-1] ↓#FF0000";
|
||||
table.c2 = "Account ↓#FF0000";
|
||||
|
||||
//初始化 图
|
||||
chart = Chart(ChartCfg);
|
||||
ChartCfg.yAxis.plotLines[0].value = 1.1;
|
||||
ChartCfg.yAxis.plotLines[1].value = -1.1;
|
||||
chart.reset();
|
||||
|
||||
var state = null;
|
||||
var isAlert = false;
|
||||
var ConnectAlert = false;
|
||||
var info = exchange.SetContractType(testContractType);//设置合约
|
||||
Log("info:",info);
|
||||
exchange.IO("mode",0);//设置行情 为立即返回模式
|
||||
var lastState = null;
|
||||
while(true){
|
||||
state = exchange.IO("status");
|
||||
if((state === true && lastState === false)){
|
||||
info = exchange.SetContractType(testContractType);
|
||||
while(info === null){
|
||||
state = exchange.IO("status");
|
||||
info = exchange.SetContractType(testContractType);
|
||||
//LogStatus("state:",state,"lastState:",lastState,"info:",info," 登录 ,重新 设置 合约类型!");
|
||||
table.a1 = state;
|
||||
table.b1 = testContractType;
|
||||
table.c1 = "等待登录 ,重新 设置 合约类型。";
|
||||
if(state === false && lastState === true){
|
||||
chart.add(0,[(new Date()).getTime(),-1]);
|
||||
}else if(state === true && lastState === false){
|
||||
chart.add(0,[(new Date()).getTime(),1]);
|
||||
}
|
||||
chart.update(ChartCfg);
|
||||
Sleep(1000 * 60);
|
||||
}
|
||||
}
|
||||
if(state === true){
|
||||
var ticker = exchange.GetTicker();
|
||||
var records = exchange.GetRecords();
|
||||
//LogStatus("exchange.IO('status') `s state is:",state,"\n",ticker,"\n",records?records[records.length - 1] : records,"\n",exchange.GetAccount(),"\n",new Date(),"\n",info);
|
||||
table.a1 = state;
|
||||
table.b1 = testContractType;
|
||||
table.b3 = ticker;
|
||||
if(records === null){
|
||||
table.b5 = "null";
|
||||
}else if(records.length > 0){
|
||||
table.b5 = records[records.length - 1];
|
||||
}
|
||||
table.c3 = exchange.GetAccount();
|
||||
if(ConnectAlert === false){
|
||||
Log("state:",state,"info:",info,"#99CC32");
|
||||
ConnectAlert = true;
|
||||
}
|
||||
//LogStatus("exchange.IO('status') `s state is:",state);
|
||||
//GetSomeContractMarketInfo(ContractArray);
|
||||
isAlert = false;
|
||||
}else{
|
||||
//LogStatus("exchange.IO('status') `s state is:",state,"time:",new Date(),"\n",info);
|
||||
table.a1 = state;
|
||||
if(isAlert === false){
|
||||
Log("state:",state,"info:",info,"#FF0000");
|
||||
Log("CTP商品期货测试微信提醒!state:" + state + "@");
|
||||
info = null;
|
||||
isAlert = true;
|
||||
}
|
||||
ConnectAlert = false;
|
||||
}
|
||||
//Log(exchange.IO("status"));
|
||||
//Log("ceshi");
|
||||
//记录状态到图表
|
||||
if(state === false && lastState === true){
|
||||
chart.add(0,[(new Date()).getTime(),-1]);
|
||||
}else if(state === true && lastState === false){
|
||||
chart.add(0,[(new Date()).getTime(),1]);
|
||||
}
|
||||
|
||||
chart.update(ChartCfg);
|
||||
$.UpdateLogStatus("time:" + (new Date())); //更新 状态栏 图表内容
|
||||
lastState = state; //当前状态更新到上次状态
|
||||
Sleep(1000 * 5);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
'''
|
||||
策略出处: https://www.botvs.com/strategy/26017
|
||||
策略名称: 获取和讯期货数据(Python版本)
|
||||
策略作者: edwardgyw
|
||||
策略描述:
|
||||
|
||||
数狂很早之前写了个js版本的第三方数据获取,我照着他的改写了个python版本的,可以下在云服务器上引入自己的包,这样研究时候也可以用。
|
||||
update 12.01 和讯接口有变化多了60分钟和周线的信息,原版选择1440会获取60分钟的k线,这里fix了
|
||||
|
||||
'''
|
||||
|
||||
#coding: utf-8
|
||||
import urllib2 as url
|
||||
import json
|
||||
import time
|
||||
|
||||
def hxRecords(instrument,timeFrame=1,size=1,includeLastBar=True,to_df=True):
|
||||
##从和讯获取期货实时数据
|
||||
pInst=instrument.lower()
|
||||
if pInst[-4]!='1':
|
||||
pInst=pInst[:-3]+'1'+pInst[-3:]
|
||||
xchg=None
|
||||
for i in instList:
|
||||
if pInst[:-4] in i['inst']:
|
||||
xchg=i['xchg']
|
||||
if xchg is None:
|
||||
print ("获取K线时发生错误: 找不到合约")
|
||||
return None
|
||||
tfs=[1,5,15,30,60,'D','W']
|
||||
tf=None
|
||||
for i in range(len(tfs)):
|
||||
if timeFrame==tfs[i]:
|
||||
tf=i
|
||||
if tf is None:
|
||||
print("获取K线时发生错误: K线周期不正确")
|
||||
return None
|
||||
now=time.localtime()
|
||||
timestr=str(now.tm_year+1)+str(12)+str(31)+'000000'
|
||||
resp='http://webftcn.hermes.hexun.com/shf/kline?code=' + xchg + pInst + '&start=' + timestr + '&number=-' + str(size) + '&type=' + str(tf)
|
||||
try:
|
||||
resp=url.urlopen(resp)
|
||||
resp=resp.read()[1:-2]
|
||||
resp=json.loads(resp)['Data']
|
||||
except:
|
||||
print '获取K线时发生错误: 不完整的JSON数据'
|
||||
return None
|
||||
re=[]
|
||||
pw=float(resp[4])
|
||||
for i in resp[0]:
|
||||
res=dict(Time=time.mktime(time.strptime(str(i[0]),'%Y%m%d%H%M%S'))*1000,Open=i[2]/pw,High=i[4]/pw
|
||||
,Low=i[5]/pw,Close=i[3]/pw,Volume=i[6])
|
||||
re.append(res)
|
||||
if to_df:
|
||||
re=pd.DataFrame(re)
|
||||
col=[]
|
||||
for i in re.columns:
|
||||
if i is 'Time':
|
||||
i='Date'
|
||||
col.append(i.lower())
|
||||
re.columns=col
|
||||
re['date']=re['date'].map(ts_dt64)
|
||||
return re
|
||||
|
||||
|
||||
instList = [{
|
||||
"xchg": "SHFE",
|
||||
"inst": ["fu", "ru", "wr"]
|
||||
}, {
|
||||
"xchg": "SHFE2",
|
||||
"inst": ["ag", "au"]
|
||||
}, {
|
||||
"xchg": "SHFE3",
|
||||
"inst": ["al", "bu", "cu", "hc", "ni", "pb", "rb", "sn", "zn"]
|
||||
}, {
|
||||
"xchg": "CZCE",
|
||||
"inst": ["cf", "fg", "lr", "ma", "oi", "pm", "ri", "rm", "rs", "sf", "sm", "sr", "ta", "wh", "zc"]
|
||||
}, {
|
||||
"xchg": "DCE",
|
||||
"inst": ["a", "b", "bb", "c", "cs", "fb", "i", "j", "jd", "jm", "l", "m", "p", "pp", "v", "y"]
|
||||
}]
|
||||
|
||||
def main():
|
||||
Log(exchange.GetAccount())
|
||||
|
||||
@@ -1,532 +0,0 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/22759
|
||||
策略名称: 震荡突破策略 (删减B值触发平仓、动态调整触发值 优化ing)
|
||||
策略作者: 小小梦
|
||||
策略描述:
|
||||
|
||||
1、吃单优化为在触发值上挂单
|
||||
2、止损仓 在被套的情况下开启止损仓。
|
||||
3、动态阀值
|
||||
4、加入 STOCHRSI 指标
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
---------------- ----- -------------
|
||||
NPeriod 4 计算周期
|
||||
Ks 0.5 上轨系数
|
||||
Kx 0.5 下轨系数
|
||||
AmountOP true 开仓量
|
||||
Interval 2000 重试间隔(毫秒)
|
||||
LoopInterval 3 轮询间隔(秒)
|
||||
PeriodShow 500 图表最大显示K线柱数
|
||||
InitMoveStop -20 初始止损值
|
||||
step 5 滑动止损步长
|
||||
isShowTouchLine true 显示触发值
|
||||
isCurrentKLineOP true 是否允许当前K线平仓后交易
|
||||
*/
|
||||
|
||||
var ChartCfg = {
|
||||
__isStock: true,
|
||||
title: {
|
||||
text: '震荡突破策略'
|
||||
},
|
||||
yAxis: [{
|
||||
title: {text: 'KL'},//标题
|
||||
style: {color: '#4572A7'},//样式
|
||||
opposite: false, //生成右边Y轴
|
||||
plotLines: [{
|
||||
value: 0,
|
||||
color: 'red',
|
||||
width: 2,
|
||||
label: {
|
||||
text: '触发值A',
|
||||
align: 'center'
|
||||
},
|
||||
}, {
|
||||
value: 0,
|
||||
color: 'green',
|
||||
width: 2,
|
||||
label: {
|
||||
text: '触发值B',
|
||||
align: 'center'
|
||||
},
|
||||
}]
|
||||
},
|
||||
{
|
||||
title:{text: "STOCHRSI"},
|
||||
opposite: true //生成右边Y轴 ceshi
|
||||
}
|
||||
],
|
||||
series: [{
|
||||
type: 'candlestick',
|
||||
name: '当前周期',
|
||||
id: 'primary',
|
||||
data: []
|
||||
}, {
|
||||
type: 'flags',
|
||||
onSeries: 'primary',
|
||||
data: [],
|
||||
},{
|
||||
type: 'spline',
|
||||
yAxis: 1,
|
||||
name: 'F',
|
||||
data: []
|
||||
},{
|
||||
type: 'spline',
|
||||
yAxis: 1,
|
||||
name: 'S',
|
||||
data: []
|
||||
}]
|
||||
};
|
||||
|
||||
/*
|
||||
plotLines: [{
|
||||
value: 0,
|
||||
color: 'red',
|
||||
width: 2,
|
||||
label: {
|
||||
text: '触发值A',
|
||||
align: 'center'
|
||||
},
|
||||
}, {
|
||||
value: 0,
|
||||
color: 'green',
|
||||
width: 2,
|
||||
label: {
|
||||
text: '触发值B',
|
||||
align: 'center'
|
||||
},
|
||||
}]
|
||||
*/
|
||||
var STATE_IDLE = 0;
|
||||
var STATE_LONG = 1;
|
||||
var STATE_SHORT = 2;
|
||||
var State = STATE_IDLE;
|
||||
|
||||
var LastBarTime = 0;
|
||||
var UpTrack = 0;
|
||||
var BottomTrack = 0;
|
||||
var chart = null;
|
||||
var InitAccount = null;
|
||||
var Counter = {
|
||||
w: 0,
|
||||
l: 0
|
||||
};
|
||||
|
||||
function LLV(array,period){
|
||||
if(!array || array.length - period < 0){
|
||||
throw "error:" + array;
|
||||
}
|
||||
var min = array[array.length - period];
|
||||
for(var i = array.length - period; i < array.length; i++){
|
||||
if( array[i] < min ){
|
||||
min = array[i];
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
function HHV(array,period){
|
||||
if(!array || array.length - period < 0){
|
||||
throw "error:" + array;
|
||||
}
|
||||
var max = array[array.length - period];
|
||||
for(var i = array.length - period; i < array.length; i++){
|
||||
if( array[i] > max){
|
||||
max = array[i];
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
function DeleteNullEle(initArr){
|
||||
var dealArr = [];
|
||||
var initArrLen = initArr.length;
|
||||
for(var i = 0,j = 0 ; i < initArrLen ; i++,j++){
|
||||
if(initArr[i] === null || isNaN(initArr[i]) ){
|
||||
j--;
|
||||
continue;
|
||||
}
|
||||
dealArr[j] = initArr[i];
|
||||
}
|
||||
return dealArr;
|
||||
}
|
||||
|
||||
function FstochRSI(records,n,m,p1,p2){
|
||||
var len = records.length;
|
||||
//var LC = records[len-2];//上一周期收盘价
|
||||
//var rsi = TA.RSI(records,n);// RSI 数组 ,talib
|
||||
var rsi = talib.RSI(records,n);
|
||||
rsi = DeleteNullEle(rsi);//ceshi
|
||||
|
||||
var arr1 = [];
|
||||
var arr2 = [];
|
||||
var arr3 = [];
|
||||
var arr4 = [];
|
||||
var rsi_a = [];
|
||||
var rsi_b = [];
|
||||
var k = [];
|
||||
var d = null;
|
||||
|
||||
for(var a = 0 ;a < rsi.length ; a++ ){//改造 不用 LLV
|
||||
if(a < m){
|
||||
continue;
|
||||
}
|
||||
for(var aa = 0 ; aa <= a; aa++ ){
|
||||
rsi_a.push(rsi[aa]);
|
||||
}
|
||||
arr1.push(rsi[a] - LLV(rsi_a,m));
|
||||
}
|
||||
for(var b = 0 ;b < rsi.length ; b++ ){//改造 不用 HHV
|
||||
if(b < m){
|
||||
continue;
|
||||
}
|
||||
for(var bb = 0 ; bb <= b; bb++ ){
|
||||
rsi_b.push(rsi[bb]);
|
||||
}
|
||||
arr2.push(HHV(rsi_b,m) - LLV(rsi_b,m));
|
||||
}
|
||||
|
||||
arr1 = DeleteNullEle(arr1);
|
||||
arr2 = DeleteNullEle(arr2);
|
||||
//Log("arr1:",arr1.length,"-",arr1);//ceshi
|
||||
//Log("arr2:",arr2.length,"-",arr2);//ceshi
|
||||
|
||||
arr3 = talib.MA(arr1,p1);
|
||||
arr4 = talib.MA(arr2,p1);
|
||||
|
||||
arr3 = DeleteNullEle(arr3);
|
||||
arr4 = DeleteNullEle(arr4);
|
||||
|
||||
//Log("ceshi");//ceshi
|
||||
var c = 0;
|
||||
var diff = 0;
|
||||
if(arr3.length !== arr4.length){//实测 长度不相等
|
||||
throw "error: !=" + arr3.length + "----" + arr4.length;
|
||||
diff = arr4.length - arr3.length; //example diff = 10 - 6
|
||||
}else{
|
||||
//throw "error:" + arr3.length + "----" + arr4.length;
|
||||
}
|
||||
|
||||
for( ;c < arr3.length ; c++ ){
|
||||
k.push(arr3[c] / arr4[c + diff] * 100);
|
||||
}
|
||||
|
||||
d = talib.MA(k,p2);
|
||||
|
||||
return [k,d,rsi];
|
||||
}
|
||||
|
||||
function CancelPendingOrders() {
|
||||
while (true) {
|
||||
var orders = exchange.GetOrders();
|
||||
for (var i = 0; i < orders.length; i++) {
|
||||
exchange.CancelOrder(orders[i].Id);
|
||||
Sleep(Interval);
|
||||
}
|
||||
if (orders.length === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isBegin = false;
|
||||
var isTrade_OC = false;
|
||||
var stochrsiFast = 0;
|
||||
var stochrsiSlow = 0;
|
||||
function onTick(exchange) {
|
||||
var records = exchange.GetRecords();
|
||||
if (!records || (records.length < 50 || records.length <= NPeriod)) { //默认 STOCHRSI 指标 最大周期14 限制
|
||||
return;
|
||||
}
|
||||
//Log(records.length);//ceshi
|
||||
//throw "stop"; //ceshi
|
||||
|
||||
var stochrsi = FstochRSI(records,14,14,3,3); // 计算 STOCHRSI 指标
|
||||
var Bar = records[records.length - 1];
|
||||
if (LastBarTime !== Bar.Time) {
|
||||
isTrade_OC = false;
|
||||
var HH = TA.Highest(records, NPeriod, 'High');
|
||||
var HC = TA.Highest(records, NPeriod, 'Close');
|
||||
var LL = TA.Lowest(records, NPeriod, 'Low');
|
||||
var LC = TA.Lowest(records, NPeriod, 'Close');
|
||||
|
||||
var Range = Math.max(HH - LC, HC - LL);
|
||||
|
||||
UpTrack = _N(Bar.Open + (Ks * Range));
|
||||
DownTrack = _N(Bar.Open - (Kx * Range));
|
||||
//更新触发值
|
||||
table.a3 = UpTrack;
|
||||
table.b3 = DownTrack;
|
||||
|
||||
if (LastBarTime > 0) {
|
||||
var PreBar = records[records.length - 2];
|
||||
chart.add(0, [PreBar.Time, PreBar.Open, PreBar.High, PreBar.Low, PreBar.Close], -1);
|
||||
chart.add(2,[records[records.length - 2].Time,stochrsi[0][stochrsi[0].length - 2]], -1); //增加 快线
|
||||
chart.add(3,[records[records.length - 2].Time,stochrsi[1][stochrsi[1].length - 2]], -1); //增加 慢线
|
||||
} else {
|
||||
for (var i = Math.min(records.length, NPeriod * 3); i > 1; i--) {
|
||||
var b = records[records.length - i];
|
||||
//Log(stochrsi[0][stochrsi[0].length - 1],stochrsi[1][stochrsi[1].length - 1]);//ceshi
|
||||
chart.add(0, [b.Time, b.Open, b.High, b.Low, b.Close]);
|
||||
if(isBegin === false){
|
||||
chart.add(1, {x:(new Date()).getTime(), color: "black", shape: 'flag', title: "title", text: "开始"});
|
||||
isBegin = true;
|
||||
}
|
||||
//chart.add(2,[records[records.length - 1].Time,stochrsi[0][stochrsi[0].length - 1]]); //增加快线
|
||||
//chart.add(3,[records[records.length - 1].Time,stochrsi[1][stochrsi[1].length - 1]]); //增加慢线
|
||||
}
|
||||
}
|
||||
chart.add(0, [Bar.Time, Bar.Open, Bar.High, Bar.Low, Bar.Close]);
|
||||
chart.add(2,[records[records.length - 2].Time,stochrsi[0][stochrsi[0].length - 2]]); //增加 快线
|
||||
chart.add(3,[records[records.length - 2].Time,stochrsi[1][stochrsi[1].length - 2]]); //增加 慢线
|
||||
if(isShowTouchLine === true){//是否显示触发值
|
||||
ChartCfg.yAxis[0].plotLines[0].value = UpTrack;
|
||||
ChartCfg.yAxis[0].plotLines[1].value = DownTrack;
|
||||
}
|
||||
ChartCfg.subtitle = {
|
||||
text: '触发值A: ' + UpTrack + ' 触发值B: ' + DownTrack
|
||||
};
|
||||
chart.update(ChartCfg);
|
||||
//chart.reset(PeriodShow); //ceshi 取消
|
||||
LastBarTime = Bar.Time;
|
||||
} else {
|
||||
chart.add(0, [Bar.Time, Bar.Open, Bar.High, Bar.Low, Bar.Close], -1);
|
||||
chart.add(2,[records[records.length - 1].Time,stochrsi[0][stochrsi[0].length - 1]], -1); // 增加 快线
|
||||
chart.add(3,[records[records.length - 1].Time,stochrsi[1][stochrsi[1].length - 1]], -1); // 增加 慢线
|
||||
}
|
||||
//Log(stochrsi);//ceshi
|
||||
//LogStatus("Price:", Bar.Close, "Up:", UpTrack, "Down:", DownTrack, "Wins: ", Counter.w, "Losses:", Counter.l, "Date:", new Date(),"\n",NowPositionInfo);
|
||||
var msg;
|
||||
var fcolor = null;
|
||||
var ftitle = "";
|
||||
stochrsiFast = table.a6 = stochrsi[0][stochrsi[0].length - 1];
|
||||
stochrsiSlow = table.b6 = stochrsi[1][stochrsi[1].length - 1];
|
||||
if((isTrade_OC === true && isCurrentKLineOP === false) && ( isTurnOP_Long === false && isTurnOP_Short === false ) ){//是否允许当前K线交易,排除反手的条件
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (State === STATE_IDLE) {
|
||||
if ( isTurnOP_Long || (Bar.Close >= UpTrack && stochrsiFast < 30 && isTurnOP_Short === false) ) { // 加入STOCHRSI 控制
|
||||
msg = '做多 触发价: ' + Bar.Close + ' 触发值A:' + UpTrack + "stochrsiFast" + stochrsiFast;
|
||||
if(isTurnOP_Long === true){
|
||||
msg = "反手做多" + msg + "#FF0000";
|
||||
}
|
||||
Log(msg);
|
||||
buyInfo = $.Buy(AmountOP);
|
||||
State = STATE_LONG;
|
||||
Log("buyInfo:",buyInfo);
|
||||
if(buyInfo === null){
|
||||
return;
|
||||
}
|
||||
if(NowPositionInfo.direction === NONE){
|
||||
NowPositionInfo.direction = LONG;
|
||||
fcolor = 'red';
|
||||
ftitle = 'L';
|
||||
moveStop = InitMoveStop; //设置 默认止损额度
|
||||
moveLevel = 0;
|
||||
lastMoveStop = 0;
|
||||
}
|
||||
chart.add(1, {x:(new Date()).getTime(), color: fcolor, shape: 'flag', title: ftitle, text: msg});
|
||||
}
|
||||
}
|
||||
|
||||
if (State === STATE_IDLE) {
|
||||
if ( isTurnOP_Short || (Bar.Close <= DownTrack && stochrsiFast > 70 && isTurnOP_Long === false) ) {//是否触发 反手 加入 STOCHRSI 控制
|
||||
msg = '做空 触发价: ' + Bar.Close + ' 触发值B:' + DownTrack + "stochrsiFast" + stochrsiFast;
|
||||
if(isTurnOP_Short === true){
|
||||
msg = "反手做空" + msg + "#FF0000";
|
||||
}
|
||||
Log(msg);
|
||||
sellInfo = $.Sell(AmountOP);
|
||||
Log("sellInfo:",sellInfo);
|
||||
if(sellInfo === null){
|
||||
return;
|
||||
}
|
||||
State = STATE_SHORT;
|
||||
if(NowPositionInfo.direction === NONE){
|
||||
NowPositionInfo.direction = SHORT;
|
||||
fcolor = 'green';
|
||||
ftitle = 'S';
|
||||
moveStop = InitMoveStop; //设置默认止损额度
|
||||
moveLevel = 0;
|
||||
lastMoveStop = 0;
|
||||
}
|
||||
chart.add(1, {x:(new Date()).getTime(), color: fcolor, shape: 'circlepin', title: ftitle, text: msg});
|
||||
}
|
||||
}
|
||||
//滑动止损
|
||||
if( (State === STATE_SHORT && NowPositionInfo.direction === SHORT ) || ( NowPositionInfo.direction === LONG && State === STATE_LONG) ){
|
||||
if(NowPositionInfo.floatProfit > step){//滑动止损 结束后 lastMoveStop 要赋值 0 , moveLevel 要赋值 0 , moveStop 重新赋值初始止损值
|
||||
moveStop = 0;
|
||||
if(NowPositionInfo.floatProfit - lastMoveStop > step){
|
||||
lastMoveStop = (++moveLevel) * step;
|
||||
}
|
||||
}
|
||||
table.b5 = "lastLine" + lastMoveStop + ", Level" + moveLevel + " ,default" + moveStop;
|
||||
//Log("NowPositionInfo",NowPositionInfo,"止损",moveStop + moveLevel * step); //ceshi
|
||||
if(NowPositionInfo.floatProfit <= moveStop + moveLevel * step ){
|
||||
//cover
|
||||
//Log("NowPositionInfo",NowPositionInfo,"止损",moveStop + moveLevel * step); //ceshi
|
||||
if(NowPositionInfo.direction === SHORT){//空仓止损
|
||||
buyInfo = $.Buy(NowPositionInfo.amount);
|
||||
if(buyInfo === null){
|
||||
return;
|
||||
}
|
||||
isTurnOP_Short = false;//重置 反手条件为false
|
||||
isTurnOP_Long = false;//重置 反手条件为false
|
||||
State = STATE_IDLE;
|
||||
Log("止损平仓",NowPositionInfo,"moveStop + moveLevel * step",moveStop + moveLevel * step,_C(exchange.GetTicker),buyInfo);//ceshi
|
||||
CalculateCounter(buyInfo);//处理Counter 计数
|
||||
NowPositionInfo.direction = NONE;
|
||||
Calculate();
|
||||
InitAccount = _C(exchange.GetAccount);
|
||||
LogProfit(Profit,InitAccount);
|
||||
fcolor = 'blue';
|
||||
ftitle = '止损 平仓';
|
||||
msg = "moveLevel:" + moveLevel + ", lastMoveStop:" + lastMoveStop;
|
||||
isTrade_OC = true;//当前bar 已经交易
|
||||
if(moveStop !== 0){//突破止损反手
|
||||
isTurnOP_Long = true;
|
||||
}
|
||||
}
|
||||
if(NowPositionInfo.direction === LONG){//多仓止损
|
||||
sellInfo = $.Sell(NowPositionInfo.amount);
|
||||
if(sellInfo === null){
|
||||
return;
|
||||
}
|
||||
isTurnOP_Long = false;//重置反手条件为false
|
||||
isTurnOP_Short = false;//重置反手条件为false
|
||||
State = STATE_IDLE;
|
||||
Log("止损平仓",NowPositionInfo,"moveStop + moveLevel * step",moveStop + moveLevel * step,_C(exchange.GetTicker),sellInfo);//ceshi
|
||||
CalculateCounter(sellInfo);//处理Counter 计数
|
||||
NowPositionInfo.direction = NONE;
|
||||
Calculate();
|
||||
InitAccount = _C(exchange.GetAccount);
|
||||
LogProfit(Profit,InitAccount);
|
||||
fcolor = 'blue';
|
||||
ftitle = '止损 平仓';
|
||||
msg = "moveLevel:" + moveLevel + ", lastMoveStop:" + lastMoveStop;
|
||||
isTrade_OC = true;//当前bar 已经交易
|
||||
if(moveStop !== 0){//突破止损反手
|
||||
isTurnOP_Short = true;
|
||||
}
|
||||
}
|
||||
chart.add(1, {x:(new Date()).getTime(), color: fcolor, shape: 'circlepin', title: ftitle, text: msg});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//反手
|
||||
var isTurnOP_Long = false;
|
||||
var isTurnOP_Short = false;
|
||||
|
||||
//滑动止损 参数
|
||||
//var step = 5; //滑动步长
|
||||
var moveStop = InitMoveStop; //初始止损
|
||||
var lastMoveStop = 0; //上次止损挡位
|
||||
var moveLevel = 0; //止损等级
|
||||
//var OpAmount = 1;//ceshi
|
||||
//var maxProfit = 0.2;
|
||||
var buyInfo = null;
|
||||
var sellInfo = null;
|
||||
var SHORT = 2;
|
||||
var LONG = 1;
|
||||
var NONE = 0;
|
||||
var NowPositionInfo = { //
|
||||
avgPrice : 0 ,
|
||||
amount : 0,
|
||||
floatProfit : 0,
|
||||
direction : 0 // 0 未持仓 , 1多仓 , 2空仓
|
||||
};
|
||||
|
||||
|
||||
function CalculateCounter(CoverInfo) {
|
||||
if(NowPositionInfo.direction === STATE_LONG && (CoverInfo.price - NowPositionInfo.avgPrice > 0)){//处理多仓
|
||||
Counter.w++;
|
||||
}else if(NowPositionInfo.direction === STATE_LONG){
|
||||
Counter.l++;
|
||||
}
|
||||
if(NowPositionInfo.direction === STATE_SHORT && (NowPositionInfo.avgPrice - CoverInfo.price > 0)){//处理空仓
|
||||
Counter.w++;
|
||||
}else if(NowPositionInfo.direction === STATE_SHORT){
|
||||
Counter.l++;
|
||||
}
|
||||
table.b4 = Counter; //更新 Counter
|
||||
}
|
||||
|
||||
|
||||
var Profit = 0;
|
||||
function Calculate(nowAccount,nowDepth){//计算并更新收益 、 浮动收益 、计算 持仓均价 、持仓量
|
||||
if(typeof(nowAccount) === 'undefined' ){
|
||||
nowAccount = _C(exchange.GetAccount);
|
||||
nowDepth = _C(exchange.GetDepth);
|
||||
}
|
||||
var diff_stocks = nowAccount.Stocks - InitAccount.Stocks;//币之差
|
||||
var diff_balance = nowAccount.Balance - InitAccount.Balance;//钱之差
|
||||
NowPositionInfo.avgPrice = _N(Math.abs(diff_balance) / Math.abs(diff_stocks),5);
|
||||
//throw nowAccount.Stocks + "----" + nowAccount.Balance;//ceshi
|
||||
NowPositionInfo.amount = _N(Math.abs(diff_stocks),5);
|
||||
NowPositionInfo.floatProfit = _N(diff_balance + diff_stocks * (NowPositionInfo.direction === LONG?nowDepth.Bids[0].Price : nowDepth.Asks[0].Price),5); //此次交易的浮动盈亏
|
||||
Profit = (nowAccount.Stocks - beginAccount.Stocks) * (NowPositionInfo.direction === LONG?nowDepth.Bids[0].Price : nowDepth.Asks[0].Price) + (nowAccount.Balance - beginAccount.Balance); //总盈亏
|
||||
table.b1 = nowAccount;//更新当前的账户信息
|
||||
table.a5 = NowPositionInfo;
|
||||
//更新入界面
|
||||
}
|
||||
|
||||
var table = null;
|
||||
function main() {
|
||||
CancelPendingOrders();
|
||||
_CInterval = 200;
|
||||
InitAccount = exchange.GetAccount();//上次平仓后账户信息
|
||||
beginAccount = InitAccount; //启动时账户信息
|
||||
LoopInterval = Math.min(1, LoopInterval);
|
||||
//清空
|
||||
LogReset();
|
||||
LogProfitReset();
|
||||
chart = Chart(ChartCfg);
|
||||
chart.reset();
|
||||
|
||||
Log('交易平台:', exchange.GetName(), InitAccount);
|
||||
LogStatus("Ready...");
|
||||
Log(InitAccount);
|
||||
|
||||
LoopInterval = Math.max(LoopInterval, 1);
|
||||
//初始化表格
|
||||
table = $.TableInit(2,7);
|
||||
table.a0 = "初始账户:↓";
|
||||
table.a1 = beginAccount;
|
||||
table.b0 = "当前账户:↓";
|
||||
table.a2 = "触发值A:↓";
|
||||
table.b2 = "触发值B:↓";
|
||||
table.a4 = "NowPositionInfo:↓";
|
||||
//table.b4 = "stop";
|
||||
//table.a5 = "Counter:";
|
||||
|
||||
|
||||
var beginTime = 0;
|
||||
while (true) {
|
||||
beginTime = (new Date()).getTime();
|
||||
onTick(exchange);
|
||||
/* //ceshi
|
||||
if(NowPositionInfo.floatProfit < -5){
|
||||
Log("ticker:",_C(exchange.GetTicker),NowPositionInfo);
|
||||
}
|
||||
//ceshi
|
||||
*/
|
||||
Calculate();
|
||||
$.UpdateLogStatus("loop used time:" + ((new Date()).getTime() - beginTime) + "ms");
|
||||
Sleep(LoopInterval * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
0.是否当前K线允许交易 处理还有问题
|
||||
1.触发平仓触发值,导致平仓,是否增加反转。
|
||||
2. 计算浮动盈亏的函数
|
||||
//改进
|
||||
1、动态 触发值调整,根据波动率
|
||||
2、参数 步长 、系数、试试 本K 重复开仓选项
|
||||
3、双仓
|
||||
*/
|
||||
@@ -11,15 +11,21 @@ exchange.GetRecords() ==> $.GetRecords(exchange, period)
|
||||
其中period的单位为分钟。
|
||||
回测K线周期应该尽可能选小(例如:回测近1个月内选1分钟,近5个月内选5分钟,等等),且period应是回测周期的倍数。
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
---- ----- ----------
|
||||
tz 8 时区 (UTC+X)
|
||||
*/
|
||||
|
||||
$.GetRecords = function (exchange, period) {
|
||||
$.GetRecords = function (exchange, period) {
|
||||
var prevT;
|
||||
var rec1 = exchange.GetRecords();
|
||||
if (!rec1) return null;
|
||||
var recN = [];
|
||||
var tmp = {Time: null, Open: null, High: null, Low: null, Close: null, Volume: 0};
|
||||
for (var i = 0; i < rec1.length; i++) {
|
||||
if (rec1[i].Time % (period*60000) === 0) {
|
||||
if (!prevT || Math.floor((rec1[i].Time+tz*3600000) / (period*60000)) > prevT) {
|
||||
prevT = Math.floor((rec1[i].Time+tz*3600000) / (period*60000));
|
||||
tmp.Time = rec1[i].Time;
|
||||
tmp.Open = rec1[i].Open;
|
||||
tmp.High = rec1[i].High;
|
||||
@@ -33,9 +39,16 @@ $.GetRecords = function (exchange, period) {
|
||||
tmp.Close = rec1[i].Close;
|
||||
tmp.Volume += rec1[i].Volume;
|
||||
}
|
||||
if ((i == rec1.length-1 || rec1[i+1].Time % (period*60000) === 0) && tmp.Time) {
|
||||
if ((i == rec1.length-1 || Math.floor((rec1[i+1].Time+tz*3600000) / (period*60000)) > prevT) && tmp.Time) {
|
||||
recN.push({Time: tmp.Time, Open: tmp.Open, High: tmp.High, Low: tmp.Low, Close: tmp.Close, Volume: tmp.Volume});
|
||||
}
|
||||
}
|
||||
return recN;
|
||||
};
|
||||
|
||||
function main() {
|
||||
Log($.GetRecords(exchange,20*1440)); // 20日K线
|
||||
Log(new Date($.GetRecords(exchange,1440)[0].Time).toString()); //测试日K线时间戳
|
||||
Log(new Date($.GetRecords(exchange,1440)[1].Time).toString());
|
||||
Log(new Date($.GetRecords(exchange,1440)[2].Time).toString());
|
||||
}
|
||||
|
||||
+316
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/6237
|
||||
策略名称: 高频收割机一号
|
||||
策略作者: Zero
|
||||
策略描述:
|
||||
|
||||
代码本身就是很好的注释, 识货的拿走.
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
------------- --------------- ------
|
||||
DicAmount 0.6,1.5,1.8,1.9 交易量
|
||||
DicBuyOffset 1.5,2.5,4.5,8 买单距离
|
||||
DicSellOffset 1,1.8,2.5,4 卖单距离
|
||||
DicResetDiff 0.1,0.3,0.3,0.4 撤单差价
|
||||
DicStep 9,12,17,25 增量步长
|
||||
DicSleep 1,2,3,5 等待周期
|
||||
SleepPeriod 3000 轮询周期
|
||||
ChartPeriod 600 统计周期
|
||||
IsEnableLog true 开启订单跟踪
|
||||
|
||||
按钮 默认值 描述
|
||||
--------- ---------- ---------
|
||||
开启/关闭订单跟踪 __button__ 开启/关闭订单跟踪
|
||||
统计周期 3600 秒数
|
||||
*/
|
||||
|
||||
|
||||
var TAmount, TBuyOffset, TSellOffset, TResetDiff, TStep, TSleep, TLen;
|
||||
var OrdersBuy = [];
|
||||
var OrdersSell = [];
|
||||
var InitAccount = null;
|
||||
var __chart = null;
|
||||
var __lastUpdate = 0;
|
||||
|
||||
function CancelPendingOrders() {
|
||||
while (true) {
|
||||
var orders = _C(exchange.GetOrders);
|
||||
if (orders.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < orders.length; i++) {
|
||||
exchange.CancelOrder(orders[i].Id);
|
||||
if (i < (orders.length-1)) {
|
||||
Sleep(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function str2array(s) {
|
||||
var ret = [];
|
||||
var arr = s.split(',');
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
ret.push(parseFloat(arr[i]));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function parseOption() {
|
||||
TAmount = str2array(DicAmount);
|
||||
TBuyOffset = str2array(DicBuyOffset);
|
||||
TSellOffset = str2array(DicSellOffset);
|
||||
TResetDiff = str2array(DicResetDiff);
|
||||
TStep = str2array(DicStep);
|
||||
TSleep = str2array(DicSleep);
|
||||
TLen = TAmount.length;
|
||||
if ((TAmount.length !== TBuyOffset.length) ||
|
||||
(TAmount.length !== TSellOffset.length) ||
|
||||
(TAmount.length !== TStep.length) ||
|
||||
(TAmount.length !== TSleep.length) ||
|
||||
(TAmount.length !== TResetDiff.length) ) {
|
||||
throw "参数有误, 表长度不一";
|
||||
}
|
||||
for (var i = 0; i < TLen; i++) {
|
||||
OrdersBuy.push({Id: 0, Price: 0, Amount: 0});
|
||||
OrdersSell.push({Id: 0, Price: 0, Amount: 0});
|
||||
}
|
||||
}
|
||||
|
||||
function getOrderPrice(books, limitAmount, prePrice, defRatio) {
|
||||
var amount = 0;
|
||||
for (var i = 0; i < books.length; i++) {
|
||||
if (i > books.length - 3) {
|
||||
return [books[1].Price * defRatio, false];
|
||||
}
|
||||
if (books[i].Price === prePrice) {
|
||||
continue;
|
||||
}
|
||||
amount += books[i].Amount;
|
||||
if (amount > limitAmount) {
|
||||
return [books[i].Price, true];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateOrders() {
|
||||
var ueseless = [];
|
||||
var orders = _C(exchange.GetOrders);
|
||||
// Update orders state, 1: Complete
|
||||
for (var i = 0; i < TLen; i++) {
|
||||
var found = false;
|
||||
var j = 0;
|
||||
if (OrdersBuy[i].Id > 0) {
|
||||
for (j = 0; j < orders.length; j++) {
|
||||
if (OrdersBuy[i].Id == orders[j].Id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
OrdersBuy[i] = {Id: 0, Price: 0};
|
||||
}
|
||||
}
|
||||
found = false;
|
||||
if (OrdersSell[i].Id > 0) {
|
||||
for (j = 0; j < orders.length; j++) {
|
||||
if (OrdersSell[i].Id == orders[j].Id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
OrdersSell[i] = {Id: 0, Price: 0};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove useless orders
|
||||
while (true) {
|
||||
var dropped = 0;
|
||||
for (i = 0; i < orders.length; i++) {
|
||||
var found = false;
|
||||
for (j = 0; j < TLen; j++) {
|
||||
if (OrdersBuy[j].Id == orders[i].Id || OrdersSell[j].Id == orders[i].Id) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
exchange.CancelOrder(orders[i].Id);
|
||||
dropped++;
|
||||
}
|
||||
}
|
||||
if (dropped === 0) {
|
||||
break;
|
||||
} else {
|
||||
Sleep(1000);
|
||||
orders = _C(exchange.GetOrders);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function onTick(pos) {
|
||||
var depth = exchange.GetDepth();
|
||||
if (!depth || depth.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// recalc order price
|
||||
var buys = [];
|
||||
var sells = [];
|
||||
for (var i = 0; i < TLen; i++) {
|
||||
var buyPrice = getOrderPrice(depth.Bids, TAmount[i], OrdersBuy[i].Price, 0.99)[0] + (0.03 * Math.random());
|
||||
var sellPrice = getOrderPrice(depth.Asks, TAmount[i], OrdersSell[i].Price, 1.01)[0] - (0.03 * Math.random());
|
||||
var newSellPrice = sellPrice;
|
||||
var newBuyPrice = buyPrice;
|
||||
for(var newSellAmount = TAmount[i]; (newSellPrice - buyPrice) <= TSellOffset[i]; newSellAmount += TStep[i]) {
|
||||
var retS = getOrderPrice(depth.Asks, newSellAmount, OrdersSell[i].Price, 1.01);
|
||||
if (!retS[1]) {
|
||||
break;
|
||||
}
|
||||
newSellPrice = retS[0] - (0.03 * Math.random());
|
||||
}
|
||||
for(var newBuyAmount = TAmount[i]; (sellPrice - newBuyPrice) <= TBuyOffset[i]; newBuyAmount += TStep[i]) {
|
||||
var retB = getOrderPrice(depth.Bids, newBuyAmount, OrdersBuy[i].Price, 0.99);
|
||||
if (!retB[1]) {
|
||||
break;
|
||||
}
|
||||
newBuyPrice = retB[0] + (0.03 * Math.random());
|
||||
}
|
||||
buys.push(_N(newBuyPrice, 2));
|
||||
sells.push(_N(newSellPrice, 2));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (!updateOrders()) {
|
||||
Sleep(1000);
|
||||
}
|
||||
var dropped = 0;
|
||||
for (i = 0; i < TLen; i++) {
|
||||
if (pos === 0 || (pos % TSleep[i] !== 0)) {
|
||||
continue;
|
||||
}
|
||||
if (OrdersBuy[i].Id > 0 && Math.abs(buys[i] - OrdersBuy[i].Price) > TResetDiff[i]) {
|
||||
//if (OrdersBuy[i].Id > 0 && (buys[i] - OrdersBuy[i].Price) > TResetDiff[i]) {
|
||||
exchange.CancelOrder(OrdersBuy[i].Id);
|
||||
dropped++;
|
||||
}
|
||||
if (OrdersSell[i].Id > 0 && Math.abs(sells[i] - OrdersSell[i].Price) > TResetDiff[i]) {
|
||||
//if (OrdersSell[i].Id > 0 && (OrdersSell[i].Price - sells[i]) > TResetDiff[i]) {
|
||||
exchange.CancelOrder(OrdersSell[i].Id);
|
||||
dropped++;
|
||||
}
|
||||
}
|
||||
if (dropped === 0) {
|
||||
break;
|
||||
} else {
|
||||
Sleep(1000);
|
||||
}
|
||||
}
|
||||
var account = _C(exchange.GetAccount);
|
||||
|
||||
var now = new Date().getTime();
|
||||
if ((now - __lastUpdate) > (1000 * ChartPeriod)) {
|
||||
__lastUpdate = now;
|
||||
var btcPrice = depth.Bids[0].Price;
|
||||
var net = _N(account.Balance + account.FrozenBalance + ((account.Stocks + account.FrozenStocks) * btcPrice));
|
||||
__chart.add([0, [now, net]]);
|
||||
__chart.add([1, [now, btcPrice]]);
|
||||
}
|
||||
|
||||
var freeAmount = account.Stocks;
|
||||
var freeBalance = account.Balance;
|
||||
|
||||
for (i = 0; i < TLen; i++) {
|
||||
if (OrdersBuy[i].Id === 0) {
|
||||
var orderAmount = Math.min(_N(freeBalance / buys[i]), TAmount[i]);
|
||||
if (orderAmount >= 0.01) {
|
||||
var orderId = exchange.Buy(buys[i], orderAmount);
|
||||
if (orderId) {
|
||||
OrdersBuy[i] = {Id: orderId, Price: buys[i], Amount: orderAmount};
|
||||
}
|
||||
}
|
||||
freeBalance -= ((buys[i] + 1) * orderAmount);
|
||||
}
|
||||
if (OrdersSell[i].Id === 0 && freeAmount >= 0.01) {
|
||||
var orderAmount = Math.min(freeAmount, TAmount[i]);
|
||||
var orderId = exchange.Sell(sells[i], orderAmount);
|
||||
if (orderId) {
|
||||
OrdersSell[i] = {Id: orderId, Price: sells[i], Amount: orderAmount};
|
||||
}
|
||||
freeAmount -= orderAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onexit() {
|
||||
CancelPendingOrders();
|
||||
Log("exit");
|
||||
}
|
||||
|
||||
function main() {
|
||||
SetErrorFilter("订单不存在|10050|net");
|
||||
exchange.IO("websocket")
|
||||
__chart = Chart({
|
||||
tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},
|
||||
title : { text : '资产趋势'},
|
||||
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: 0,
|
||||
inputEnabled: false
|
||||
},
|
||||
xAxis: { type: 'datetime'},
|
||||
yAxis: [{ // Primary yAxis
|
||||
labels: { format: '{value}元', style: { color: '#FF0000' } },
|
||||
title: { text: '资产估值', style: { color: '#FF0000' } }
|
||||
}, { title: { text: 'BTC单价', style: { color: '#4572A7' } },
|
||||
labels: { format: '{value} 元', style: { color: '#4572A7' } },
|
||||
opposite: false
|
||||
}],
|
||||
series : [{
|
||||
name : '净产净值',
|
||||
data : [],
|
||||
tooltip: {
|
||||
valueDecimals: 2
|
||||
}
|
||||
},{
|
||||
name : 'BTC单价',
|
||||
yAxis: 1,
|
||||
data : [],
|
||||
tooltip: {
|
||||
valueDecimals: 2
|
||||
}
|
||||
}]
|
||||
});
|
||||
__chart.reset();
|
||||
EnableLog(IsEnableLog);
|
||||
Log("Switch to websocket => ", exchange.IO("websocket"));
|
||||
CancelPendingOrders();
|
||||
InitAccount = _C(exchange.GetAccount);
|
||||
Log(InitAccount);
|
||||
parseOption();
|
||||
var allAmount = _.reduce(TAmount, function(memo, num){ return memo + num; }, 0);
|
||||
var ticker = _C(exchange.GetTicker);
|
||||
var ratio = (InitAccount.Balance + InitAccount.FrozenBalance + ((InitAccount.Stocks + InitAccount.FrozenStocks) * ticker.Buy)) / 2 / ticker.Buy / allAmount;
|
||||
for (var i = 0; i < TAmount.length; i++) {
|
||||
TAmount[i] = _N(TAmount[i] * ratio, 3);
|
||||
}
|
||||
Log("订单跟踪已经: " + (IsEnableLog ? "开启" : "关闭"), " 统计周期为", ChartPeriod, "秒, ", TAmount, "缩放比例:", _N(ratio));
|
||||
for (var count = 0; ; count++) {
|
||||
var cmd = GetCommand();
|
||||
if (cmd == '开启/关闭订单跟踪') {
|
||||
IsEnableLog = !IsEnableLog;
|
||||
EnableLog(IsEnableLog);
|
||||
Log("订单跟踪已经: " + (IsEnableLog ? "开启" : "关闭"));
|
||||
} else if (cmd && cmd.indexOf('统计周期:') === 0) {
|
||||
ChartPeriod = parseFloat(cmd.split(':')[1]);
|
||||
Log("统计周期变更为", ChartPeriod, "秒");
|
||||
}
|
||||
onTick(count);
|
||||
Sleep(SleepPeriod);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user