update
This commit is contained in:
@@ -4,21 +4,22 @@
|
||||
> 开源策略列表
|
||||
|
||||
|
||||
- [商品期货套利 - 多品种网格对冲模型](https://www.botvs.com/strategy/27799)
|
||||
- [策略框架模板](https://www.botvs.com/strategy/30861)
|
||||
- [策略框架模板 (复制)](https://www.botvs.com/strategy/33431)
|
||||
- [画线类库](https://www.botvs.com/strategy/27293)
|
||||
- [python版 Dual Thrust OKCoin 期货](https://www.botvs.com/strategy/21856)
|
||||
- [测试-布林带策略(基于策略框架) (复制)](https://www.botvs.com/strategy/31456)
|
||||
- [均线策略 30 行搞定 (复制)](https://www.botvs.com/strategy/29718)
|
||||
- [国外知名策略R-Breaker (复制)](https://www.botvs.com/strategy/30512)
|
||||
- [数字货币交易类库 (期货支持OKCoin期货/BitVC)](https://www.botvs.com/strategy/30199)
|
||||
- [范例-定时测试微信消息推送](https://www.botvs.com/strategy/15098)
|
||||
- [测试-布林带策略(基于策略框架)](https://www.botvs.com/strategy/28128)
|
||||
- [测试-CTP商品期货连接状态 (图表显示)](https://www.botvs.com/strategy/22838)
|
||||
- [CTP商品期货多品种海龟交易策略](https://www.botvs.com/strategy/17289)
|
||||
- [商品期货跨期对冲 - 单品种套利合约](https://www.botvs.com/strategy/27122)
|
||||
- [python版CTP商品期货交易类库](https://www.botvs.com/strategy/24288)
|
||||
- [Python API 测试](https://www.botvs.com/strategy/21365)
|
||||
- [画线类库](https://www.botvs.com/strategy/27293)
|
||||
- [商品期货交易类库](https://www.botvs.com/strategy/12961)
|
||||
- [从第三方获取K线 (商品期货版) Rev. 161216](https://www.botvs.com/strategy/15498)
|
||||
- [国外知名策略R-Breaker](https://www.botvs.com/strategy/23898)
|
||||
- [均线策略 30 行搞定](https://www.botvs.com/strategy/12348)
|
||||
- [商品期货跨期对冲 - 百行代码实现](https://www.botvs.com/strategy/23896)
|
||||
- [短信通知类库](https://www.botvs.com/strategy/26921)
|
||||
@@ -41,7 +42,6 @@
|
||||
- [Python简单测试策略](https://www.botvs.com/strategy/20761)
|
||||
- [数字货币现货交易类库](https://www.botvs.com/strategy/10989)
|
||||
- [高频交易策略之 - Penny Jump](https://www.botvs.com/strategy/358)
|
||||
- [图表模板(增加状态栏表格功能)](https://www.botvs.com/strategy/16848)
|
||||
- [MACD低买高卖自动跟单滑动止损 (复制)](https://www.botvs.com/strategy/20266)
|
||||
- [获取周K线](https://www.botvs.com/strategy/20226)
|
||||
- [对冲2.7收益率统计系统 支持多组账号统计](https://www.botvs.com/strategy/18678)
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
'''
|
||||
策略出处: https://www.botvs.com/strategy/21856
|
||||
策略名称: python版 Dual Thrust OKCoin 期货
|
||||
策略作者: 小小梦
|
||||
策略描述:
|
||||
|
||||
Dual Thrust 策略包含完整的图表显示, 图表动态更新,模板引用等功能, 可做学习模板使用.
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
--------------- ----- --------------
|
||||
ContractTypeIdx 0 合约品种: 当周|次周|季度
|
||||
MarginLevelIdx 0 杠杆大小: 10|20
|
||||
NPeriod 4 计算周期
|
||||
Ks 0.5 上轨系数
|
||||
Kx 0.5 下轨系数
|
||||
AmountOP true 开仓合约张数
|
||||
Interval 2000 重试间隔(毫秒)
|
||||
LoopInterval 3 轮询间隔(秒)
|
||||
PeriodShow 500 图表最大显示K线柱数
|
||||
'''
|
||||
|
||||
import time
|
||||
class Error_noSupport(BaseException):
|
||||
def __init__(self):
|
||||
Log("只支持OKCoin期货!#FF0000")
|
||||
|
||||
class Error_AtBeginHasPosition(BaseException):
|
||||
def __init__(self):
|
||||
Log("启动时有期货持仓! #FF0000")
|
||||
|
||||
ChartCfg = {
|
||||
'__isStock': True,
|
||||
'title': {
|
||||
'text': 'Dual Thrust 上下轨图'
|
||||
},
|
||||
'yAxis': {
|
||||
'plotLines': [{
|
||||
'value': 0,
|
||||
'color': 'red',
|
||||
'width': 2,
|
||||
'label': {
|
||||
'text': '上轨',
|
||||
'align': 'center'
|
||||
},
|
||||
}, {
|
||||
'value': 0,
|
||||
'color': 'green',
|
||||
'width': 2,
|
||||
'label': {
|
||||
'text': '下轨',
|
||||
'align': 'center'
|
||||
},
|
||||
}]
|
||||
},
|
||||
'series': [{
|
||||
'type': 'candlestick',
|
||||
'name': '当前周期',
|
||||
'id': 'primary',
|
||||
'data': []
|
||||
}, {
|
||||
'type': 'flags',
|
||||
'onSeries': 'primary',
|
||||
'data': []
|
||||
}]
|
||||
}
|
||||
|
||||
STATE_IDLE = 0
|
||||
STATE_LONG = 1
|
||||
STATE_SHORT = 2
|
||||
State = STATE_IDLE
|
||||
|
||||
LastBarTime = 0
|
||||
UpTrack = 0
|
||||
BottomTrack = 0
|
||||
chart = None
|
||||
InitAccount = None
|
||||
LastAccount = None
|
||||
Counter = {
|
||||
'w': 0,
|
||||
'l': 0
|
||||
}
|
||||
|
||||
def GetPosition(posType): # if the positions has no this posType ,will return [] ,Another case is return a dict of object
|
||||
positions = exchange.GetPosition()
|
||||
return [{'Price': position['Price'], 'Amount': position['Amount']} for position in positions if position['Type'] == posType]
|
||||
|
||||
def CancelPendingOrders():
|
||||
while True:
|
||||
orders = exchange.GetOrders()
|
||||
[exchange.CancelOrder(order['Id']) for order in orders if not Sleep(500)]
|
||||
if len(orders) == 0:
|
||||
break
|
||||
|
||||
def Trade(currentState,nextState):
|
||||
global InitAccount,LastAccount,OpenPrice,ClosePrice
|
||||
ticker = _C(exchange.GetTicker)
|
||||
slidePrice = 1
|
||||
pfn = exchange.Buy if nextState == STATE_LONG else exchange.Sell
|
||||
if currentState != STATE_IDLE:
|
||||
Log(_C(exchange.GetPosition)) # ceshi
|
||||
exchange.SetDirection("closebuy" if currentState == STATE_LONG else "closesell")
|
||||
while True:
|
||||
# ID = pfn( (ticker['Last'] - slidePrice) if currentState == STATE_LONG else (ticker['Last'] + slidePrice), AmountOP) # xiugai 限价单
|
||||
# ID = pfn(-1, AmountOP) # xiugai 市价单
|
||||
ID = pfn(AmountOP) # xiugai 市价单
|
||||
Sleep(Interval)
|
||||
Log(exchange.GetOrder(ID)) # xiugai
|
||||
ClosePrice = (exchange.GetOrder(ID))['AvgPrice'] #
|
||||
CancelPendingOrders()
|
||||
if len(GetPosition(PD_LONG if currentState == STATE_LONG else PD_SHORT)) == 0:
|
||||
break
|
||||
account = exchange.GetAccount()
|
||||
if account['Stocks'] > LastAccount['Stocks']:
|
||||
Counter['w'] += 1
|
||||
else:
|
||||
Counter['l'] += 1
|
||||
# Log("ceshi account:",account,InitAccount) #ceshi
|
||||
Log(account) # xiugai
|
||||
LogProfit((account['Stocks'] - InitAccount['Stocks']),"收益率:", ((account['Stocks'] - InitAccount['Stocks']) * 100 / InitAccount['Stocks']),'%')
|
||||
Cal(OpenPrice,ClosePrice)
|
||||
LsatAccount = account
|
||||
|
||||
exchange.SetDirection("buy" if nextState == STATE_LONG else "sell")
|
||||
Log(_C(exchange.GetAccount))
|
||||
while True:
|
||||
# pfn( (ticker['Last'] + slidePrice) if nextState == STATE_LONG else (ticker['Last'] - slidePrice), AmountOP) # 限价单
|
||||
# ID = pfn(-1, AmountOP) # 市价单
|
||||
ID = pfn(AmountOP) # 市价单
|
||||
Sleep(Interval)
|
||||
Log(exchange.GetOrder(ID)) # xiugai
|
||||
CancelPendingOrders()
|
||||
pos = GetPosition(PD_LONG if nextState == STATE_LONG else PD_SHORT)
|
||||
if len(pos) != 0:
|
||||
Log("持仓均价",pos[0]['Price'],"数量:",pos[0]['Amount'])
|
||||
OpenPrice = (exchange.GetOrder(ID))['AvgPrice'] # pos[0]['Price']
|
||||
Log("now account:",exchange.GetAccount())
|
||||
break
|
||||
|
||||
def onTick(exchange):
|
||||
global LastBarTime,chart,State,UpTrack,DownTrack,LastAccount
|
||||
records = exchange.GetRecords()
|
||||
if not records or len(records) <= NPeriod:
|
||||
return
|
||||
Bar = records[-1]
|
||||
if LastBarTime != Bar['Time']:
|
||||
HH = TA.Highest(records, NPeriod, 'High')
|
||||
HC = TA.Highest(records, NPeriod, 'Close')
|
||||
LL = TA.Lowest(records, NPeriod, 'Low')
|
||||
LC = TA.Lowest(records, NPeriod, 'Close')
|
||||
|
||||
Range = max(HH - LC, HC - LL)
|
||||
UpTrack = _N(Bar['Open'] + (Ks * Range))
|
||||
DownTrack = _N(Bar['Open'] - (Kx * Range))
|
||||
if LastBarTime > 0:
|
||||
PreBar = records[-2]
|
||||
chart.add(0, [PreBar['Time'], PreBar['Open'], PreBar['High'], PreBar['Low'], PreBar['Close']], -1)
|
||||
else:
|
||||
for i in range(len(records) - min(len(records), NPeriod * 3), len(records)):
|
||||
b = records[i]
|
||||
chart.add(0,[b['Time'], b['Open'], b['High'], b['Low'], b['Close']])
|
||||
|
||||
chart.add(0,[Bar['Time'], Bar['Open'], Bar['High'], Bar['Low'], Bar['Close']])
|
||||
ChartCfg['yAxis']['plotLines'][0]['value'] = UpTrack
|
||||
ChartCfg['yAxis']['plotLines'][1]['value'] = DownTrack
|
||||
ChartCfg['subtitle'] = {
|
||||
'text': '上轨' + str(UpTrack) + '下轨' + str(DownTrack)
|
||||
}
|
||||
chart.update(ChartCfg)
|
||||
chart.reset(PeriodShow)
|
||||
|
||||
LastBarTime = Bar['Time']
|
||||
else:
|
||||
chart.add(0,[Bar['Time'], Bar['Open'], Bar['High'], Bar['Low'], Bar['Close']], -1)
|
||||
|
||||
LogStatus("Price:", Bar["Close"], "up:", UpTrack, "down:", DownTrack, "wins:", Counter['w'], "losses:", Counter['l'], "Date:", time.time())
|
||||
msg = ""
|
||||
if State == STATE_IDLE or State == STATE_SHORT:
|
||||
if Bar['Close'] >= UpTrack:
|
||||
msg = "做多,触发价:" + str(Bar['Close']) + "上轨" + str(UpTrack)
|
||||
Log(msg)
|
||||
Trade(State, STATE_LONG)
|
||||
State = STATE_LONG
|
||||
chart.add(1,{'x': Bar['Time'], 'color': 'red', 'shape': 'flag', 'title': '多', 'text': msg})
|
||||
|
||||
if State == STATE_IDLE or State == STATE_LONG:
|
||||
if Bar['Close'] <= DownTrack:
|
||||
msg = "做空,触发价:" + str(Bar['Close']) + "下轨" + str(DownTrack)
|
||||
Log(msg)
|
||||
Trade(State, STATE_SHORT)
|
||||
State = STATE_SHORT
|
||||
chart.add(1,{'x': Bar['Time'], 'color': 'green', 'shape': 'circlepin', 'title': '空', 'text': msg})
|
||||
|
||||
OpenPrice = 0
|
||||
ClosePrice = 0
|
||||
def Cal(OpenPrice, ClosePrice):
|
||||
global AmountOP,State
|
||||
if State == STATE_SHORT:
|
||||
Log(AmountOP,OpenPrice,ClosePrice,"策略盈亏:", (AmountOP * 100) / ClosePrice - (AmountOP * 100) / OpenPrice, "个币, 手续费:", - (100 * AmountOP * 0.0003), "美元,折合:", _N( - 100 * AmountOP * 0.0003/OpenPrice,8), "个币")
|
||||
Log(((AmountOP * 100) / ClosePrice - (AmountOP * 100) / OpenPrice) + (- 100 * AmountOP * 0.0003/OpenPrice))
|
||||
if State == STATE_LONG:
|
||||
Log(AmountOP,OpenPrice,ClosePrice,"策略盈亏:", (AmountOP * 100) / OpenPrice - (AmountOP * 100) / ClosePrice, "个币, 手续费:", - (100 * AmountOP * 0.0003), "美元,折合:", _N( - 100 * AmountOP * 0.0003/OpenPrice,8), "个币")
|
||||
Log(((AmountOP * 100) / OpenPrice - (AmountOP * 100) / ClosePrice) + (- 100 * AmountOP * 0.0003/OpenPrice))
|
||||
|
||||
def main():
|
||||
global LoopInterval,chart,LastAccount,InitAccount
|
||||
if exchange.GetName() != 'Futures_OKCoin':
|
||||
raise Error_noSupport
|
||||
exchange.SetRate(1)
|
||||
exchange.SetContractType(["this_week","next_week","quarter"][ContractTypeIdx])
|
||||
exchange.SetMarginLevel([10,20][MarginLevelIdx])
|
||||
|
||||
Log("Fee:",exchange.GetFee())
|
||||
if len(exchange.GetPosition()) > 0:
|
||||
raise Error_AtBeginHasPosition
|
||||
CancelPendingOrders()
|
||||
InitAccount = LastAccount = exchange.GetAccount()
|
||||
LoopInterval = min(1,LoopInterval)
|
||||
Log("交易平台:",exchange.GetName(), InitAccount)
|
||||
LogStatus("Ready...")
|
||||
|
||||
LogProfitReset()
|
||||
chart = Chart(ChartCfg)
|
||||
chart.reset()
|
||||
|
||||
LoopInterval = max(LoopInterval, 1)
|
||||
while True:
|
||||
onTick(exchange)
|
||||
Sleep(LoopInterval * 1000)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/27799
|
||||
策略名称: 商品期货套利 - 多品种网格对冲模型
|
||||
策略作者: Zero
|
||||
策略描述:
|
||||
|
||||
商品期货对冲 + 网格
|
||||
|
||||
传统的跨期对冲一般指统计套利, 用线性回归或者其它办法生成一个套利区间, 这样套利机会比较少, 而且有预测性, 未来价差很可能不是预计的那样回归
|
||||
|
||||
为了解决这种办法, 进而更频繁的进行套利操作, 我们把两个关联品种或者跨期品种的套利价差定义成一个网格, 每满足一定的价差就开一次仓, 做一次对冲
|
||||
|
||||
这样价差来回在我们设置的网格里进行波动,我们就能不断的开仓平仓实现盈利.
|
||||
|
||||
不善方案, 不表达了, 具体看策略代码
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
----------- -------------------------------------------------------------------------------------- -----------
|
||||
HedgeTable (MA705&MA709)30:15:1;40:25:1;50:35:1;60:45:1(FG705&FG709)10:0:1;15:5:1;20:10:1;25:15:1 开仓表
|
||||
CoverAll false 启动时平掉所有仓位
|
||||
CalcPeriod 60 账户权益统计周期(分)
|
||||
AutoRestore false 自动恢复
|
||||
*/
|
||||
|
||||
function Hedge(q, e, positions, symbolA, symbolB, hedgeSpread) {
|
||||
var self = {}
|
||||
self.q = q
|
||||
self.symbolA = symbolA
|
||||
self.symbolB = symbolB
|
||||
self.name = symbolA + " & " + symbolB
|
||||
self.e = e
|
||||
self.isBusy = false
|
||||
self.diffA = 0
|
||||
self.diffB = 0
|
||||
self.update = _D()
|
||||
var arr = hedgeSpread.split(';')
|
||||
self.dic = []
|
||||
var n = 0
|
||||
var coefficient = 1
|
||||
for (var i = 0; i < positions.length; i++) {
|
||||
if (positions[i].ContractType == symbolA) {
|
||||
n += positions[i].Amount
|
||||
if (positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) {
|
||||
coefficient = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
_.each(arr, function(pair) {
|
||||
var tmp = pair.split(':');
|
||||
if (tmp.length != 3) {
|
||||
throw "开仓表不正确";
|
||||
}
|
||||
var st = {
|
||||
open: Number(tmp[0]),
|
||||
cover: Number(tmp[1]),
|
||||
amount: Number(tmp[2]),
|
||||
hold: 0
|
||||
}
|
||||
if (n > 0) {
|
||||
n -= Math.min(n, st.amount)
|
||||
st.hold = st.amount * coefficient
|
||||
Log("恢复", self.name, st)
|
||||
}
|
||||
self.dic.push(st)
|
||||
});
|
||||
if (n > 0) {
|
||||
throw "恢复失败, 有多余仓位 " + n;
|
||||
}
|
||||
|
||||
self.poll = function() {
|
||||
if (self.isBusy || (!$.IsTrading(self.symbolA))) {
|
||||
return
|
||||
}
|
||||
var insDetailA = exchange.SetContractType(self.symbolA)
|
||||
if (!insDetailA) {
|
||||
return
|
||||
}
|
||||
var tickerA = exchange.GetTicker()
|
||||
if (!tickerA) {
|
||||
return
|
||||
}
|
||||
var insDetailB = exchange.SetContractType(self.symbolB)
|
||||
if (!insDetailB) {
|
||||
return
|
||||
}
|
||||
var tickerB = exchange.GetTicker()
|
||||
if (!tickerB) {
|
||||
return
|
||||
}
|
||||
|
||||
self.update = _D(tickerA.Time)
|
||||
|
||||
var action = null
|
||||
var diffA = _N(tickerA.Buy - tickerB.Sell)
|
||||
var diffB = _N(tickerA.Sell - tickerB.Buy)
|
||||
self.diffA = diffA
|
||||
self.diffB = diffB
|
||||
|
||||
for (var i = 0; i < self.dic.length && !action; i++) {
|
||||
if (self.dic[i].hold == 0) {
|
||||
if (self.dic[i].open <= diffA) {
|
||||
action = [i, "sell", "buy", self.dic[i].amount]
|
||||
} else if (self.dic[i].open <= -diffB) {
|
||||
action = [i, "buy", "sell", -self.dic[i].amount]
|
||||
}
|
||||
} else {
|
||||
if (self.dic[i].hold > 0 && self.dic[i].cover >= diffB) {
|
||||
action = [i, "closesell", "closebuy", self.dic[i].hold]
|
||||
} else if (self.dic[i].hold < 0 && self.dic[i].cover >= -diffA) {
|
||||
action = [i, "closebuy", "closesell", self.dic[i].hold]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!action) {
|
||||
return
|
||||
}
|
||||
|
||||
Log("A卖B买: " + _N(diffA) + ", A买B卖: " + _N(diffB), ", Action: " + JSON.stringify(action))
|
||||
|
||||
self.isBusy = true
|
||||
|
||||
self.q.pushTask(self.e, self.symbolA, action[1], self.dic[action[0]].amount, function(task, ret) {
|
||||
if (!ret) {
|
||||
self.isBusy = false
|
||||
return
|
||||
}
|
||||
self.q.pushTask(self.e, self.symbolB, action[2], self.dic[action[0]].amount, function(task, ret) {
|
||||
if (!ret) {
|
||||
throw "开仓失败..."
|
||||
}
|
||||
self.isBusy = false
|
||||
if (task.action != "buy" && task.action != "sell") {
|
||||
self.dic[action[0]].hold = 0;
|
||||
} else {
|
||||
self.dic[action[0]].hold = action[3];
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
function main() {
|
||||
SetErrorFilter("ready|login|timeout")
|
||||
Log("正在与交易服务器连接...")
|
||||
while (!exchange.IO("status")) Sleep(1000);
|
||||
Log("与交易服务器连接成功")
|
||||
var mode = exchange.IO("mode", 0);
|
||||
if (typeof(mode) !== 'number') {
|
||||
throw "切换模式失败, 请更新到最新托管者!";
|
||||
} else {
|
||||
Log("已切换到适合多品种价格查询的立即模式");
|
||||
}
|
||||
|
||||
if (CoverAll) {
|
||||
Log("开始平掉所有残余仓位...");
|
||||
$.NewPositionManager().CoverAll();
|
||||
Log("操作完成");
|
||||
}
|
||||
LogStatus("尝试获取持仓状态")
|
||||
var positions = _C(exchange.GetPosition)
|
||||
LogStatus("Ready")
|
||||
|
||||
if (positions.length > 0 && !AutoRestore) {
|
||||
throw "发现持仓, 请勾选自动恢复"
|
||||
}
|
||||
|
||||
var pairs = []
|
||||
var q = $.NewTaskQueue(function(task, ret) {
|
||||
Log(task.desc, ret ? "成功" : "失败")
|
||||
})
|
||||
var arr = HedgeTable.split(',');
|
||||
var tbl = {
|
||||
type: 'table',
|
||||
title: 'Runtime',
|
||||
cols: ['Pair', 'Open', 'Cover', 'Hold', 'DiffA', 'DiffB', 'Time'],
|
||||
rows: []
|
||||
};
|
||||
_.each(arr, function(item) {
|
||||
var tmp = item.split(')');
|
||||
var pair = tmp[0].replace('(', '').split('&');
|
||||
var symbolDetail = _C(exchange.SetContractType, pair[0])
|
||||
Log("合约", symbolDetail.InstrumentName, "一手", symbolDetail.VolumeMultiple, "份, 最大下单量", symbolDetail.MaxLimitOrderVolume, "保证金率:", _N(symbolDetail.LongMarginRatio), _N(symbolDetail.ShortMarginRatio), "交割日期", symbolDetail.StartDelivDate);
|
||||
symbolDetail = _C(exchange.SetContractType, pair[1])
|
||||
Log("合约", symbolDetail.InstrumentName, "一手", symbolDetail.VolumeMultiple, "份, 最大下单量", symbolDetail.MaxLimitOrderVolume, "保证金率:", _N(symbolDetail.LongMarginRatio), _N(symbolDetail.ShortMarginRatio), "交割日期", symbolDetail.StartDelivDate);
|
||||
pairs.push(Hedge(q, exchanges[0], positions, pair[0], pair[1], tmp[1]))
|
||||
});
|
||||
var ts = 0
|
||||
var lastUpdate = 0
|
||||
while (true) {
|
||||
if (!exchange.IO("status")) {
|
||||
Sleep(1000)
|
||||
continue
|
||||
}
|
||||
|
||||
var now = new Date().getTime()
|
||||
if (now - ts > (CalcPeriod * 60000)) {
|
||||
var account = exchange.GetAccount()
|
||||
if (account) {
|
||||
var obj = JSON.parse(exchange.GetRawJSON())
|
||||
$.PlotLine('账户权益', obj['Balance'] + obj['PositionProfit']);
|
||||
ts = now
|
||||
}
|
||||
}
|
||||
// IO("wait") 会一直等待收到任何一个品种的行情推送信息, 返回收到行情的真实时间
|
||||
var n = exchange.IO("wait")
|
||||
// 计算行情信息传到策略层花费的时间
|
||||
var idle = UnixNano() - n
|
||||
|
||||
if (now - lastUpdate > 5000) {
|
||||
tbl.rows = []
|
||||
_.each(pairs, function(t) {
|
||||
for (var i = 0; i < t.dic.length; i++) {
|
||||
tbl.rows.push([t.name, t.dic[i].open, t.dic[i].cover, t.dic[i].hold, t.diffA, t.diffB, t.update])
|
||||
}
|
||||
});
|
||||
LogStatus('`' + JSON.stringify(tbl) + '`\nUpdate: ' + _D() + ', Idle: ' + (idle/1000000) + ' ms')
|
||||
lastUpdate = now
|
||||
}
|
||||
|
||||
_.each(pairs, function(t) {
|
||||
t.poll()
|
||||
});
|
||||
|
||||
q.poll()
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/23898
|
||||
策略名称: 国外知名策略R-Breaker
|
||||
策略作者: wmjbs
|
||||
策略描述:
|
||||
|
||||
根据国外知名策略R-Breaker改写,当天收盘时不平仓。默认K线周期选择1天效果最佳。
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
------- ----- ----------
|
||||
NPeriod true 计算周期
|
||||
f1 0.47 中轨上下顶部区间系数
|
||||
f2 0.07 中轨上下区间系数
|
||||
f3 0.25 上下轨系数
|
||||
Gain false 上次或初始收益
|
||||
*/
|
||||
|
||||
function onTick() {
|
||||
var records = _C(exchange.GetRecords);
|
||||
var ticker = _C(exchange.GetTicker);
|
||||
var account = _C(exchange.GetAccount);
|
||||
|
||||
var H = TA.Highest(records, NPeriod, 'High');
|
||||
var C = TA.Lowest(records, 1, 'Close');
|
||||
var L = TA.Lowest(records, NPeriod, 'Low');
|
||||
|
||||
|
||||
var ssetup = H + f1 * (C - L);
|
||||
var senter = ((1 + f2) / 2) * (H + C) - f2 * L;
|
||||
var benter = ((1 + f2) / 2) * (L + C) - f2 * H;
|
||||
var bsetup = L - f1 * (H - C);
|
||||
var bbreak = ssetup + f3 * (ssetup - bsetup);
|
||||
var sbreak = bsetup - f3 * (ssetup - bsetup);
|
||||
|
||||
|
||||
LogStatus('账户信息:', account, '\n最新价: ', ticker.Last, '状态:', state, '\n上轨: ', bbreak, '中轨顶', ssetup, '中轨上:', senter, '中轨下:', benter, '中轨底', bsetup, '下轨: ', sbreak);
|
||||
|
||||
income = (account.Balance + account.FrozenBalance) + (account.Stocks + account.FrozenStocks) * ticker.Last - initBalance - initStocks * initticker + Gain;
|
||||
|
||||
if (ticker.Last > ssetup) {
|
||||
state = 1;
|
||||
} else if (ticker.Last >= bsetup && ticker.Last <= ssetup) {
|
||||
state = 0;
|
||||
} else if (ticker.Last < bsetup && account.Stocks < 0.01) {
|
||||
state = -1;
|
||||
}
|
||||
|
||||
//做多
|
||||
if (ticker.Last > bbreak && account.Balance / ticker.Last >= 0.01) {
|
||||
Log('开始做多');
|
||||
$.Buy(account.Balance / ticker.Last);
|
||||
Log('保持多仓');
|
||||
// LogProfit(income);
|
||||
state = 1;
|
||||
}
|
||||
|
||||
//做空
|
||||
else if (state === 1 && ticker.Last < senter && account.Stocks >= 0.01) {
|
||||
Log('开始做空');
|
||||
$.Sell(account.Stocks);
|
||||
Log('保持空仓');
|
||||
LogProfit(income);
|
||||
}
|
||||
|
||||
|
||||
//做多
|
||||
if (state === -1 && ticker.Last > benter && account.Balance / ticker.Last >= 0.01) {
|
||||
Log('开始做多');
|
||||
$.Buy(account.Balance / ticker.Last);
|
||||
Log('保持多仓');
|
||||
// LogProfit(income);
|
||||
}
|
||||
|
||||
//做空
|
||||
else if (ticker.Last < sbreak && account.Stocks >= 0.01) {
|
||||
Log('开始做空');
|
||||
$.Sell(account.Stocks);
|
||||
Log('保持空仓');
|
||||
LogProfit(income);
|
||||
state = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function main() {
|
||||
var account = _C(exchange.GetAccount);
|
||||
var ticker = _C(exchange.GetTicker);
|
||||
initticker = ticker.Last;
|
||||
initBalance = account.Balance;
|
||||
initStocks = account.Stocks;
|
||||
state = 0;
|
||||
biaoji = 0;
|
||||
|
||||
if (initStocks < 0.01 || (initBalance / initticker < 0.01)) {
|
||||
Log('账户必须有钱又有币,请重新设置!#ff0000');
|
||||
onexit();
|
||||
}
|
||||
LogProfit(Gain);
|
||||
Log('上次或初始收益');
|
||||
|
||||
while (true) {
|
||||
onTick();
|
||||
Sleep(1000);
|
||||
}
|
||||
}
|
||||
@@ -1,346 +0,0 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/16848
|
||||
策略名称: 图表模板(增加状态栏表格功能)
|
||||
策略作者: 小小梦
|
||||
策略描述:
|
||||
|
||||
图表模板
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
-------------- ----- --------
|
||||
indicatorsName 指标轴1 指标轴
|
||||
indicators_1 指标1 指标1
|
||||
indicators_2 指标2 指标2
|
||||
Interval 500 间隔(毫秒)
|
||||
isOpenRightY true 是否开启右边Y轴
|
||||
lineType line 指标线类型
|
||||
*/
|
||||
|
||||
/*
|
||||
这个是一个图表模板,详细用法见广场帖子。
|
||||
*/
|
||||
//----------------------------------图表模块----------------------------------------------------------------
|
||||
var ChartObj = {//画图
|
||||
tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},
|
||||
chart: { zoomType:'x',panning:true },//图表类型
|
||||
title: { text: title}, //标题
|
||||
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
|
||||
},
|
||||
subtitle: {text: subtitle},//副标题
|
||||
xAxis:{type: 'datetime'},
|
||||
yAxis: [{
|
||||
title: {text: 'K线'},//标题
|
||||
style: {color: '#4572A7'},//样式
|
||||
opposite: false //生成右边Y轴
|
||||
},
|
||||
{
|
||||
title:{text: indicatorsName},
|
||||
opposite: isOpenRightY //生成右边Y轴 ceshi
|
||||
}
|
||||
],
|
||||
series: [//系列
|
||||
{type:'candlestick',yAxis:0,name:'K',id:'KLine',data:[]},
|
||||
{type:'flags',onSeries:'KLine',data:[]},
|
||||
{name:indicators_1,type:lineType,yAxis:isOpenRightY?1:0,data:[]},
|
||||
{name:indicators_2,type:lineType,yAxis:isOpenRightY?1:0,data:[]}
|
||||
//{name:indicators_1,type:'spline',yAxis:1,data:[]},
|
||||
//{name:indicators_2,type:'spline',yAxis:1,data:[]}
|
||||
]
|
||||
};
|
||||
var chart = Chart(ChartObj);
|
||||
var isFirst = true;
|
||||
var preRecordTime = 0;
|
||||
var lastRecordsTime = 0;
|
||||
var title = exchange.GetName();
|
||||
var subtitle = indicators_1+"、"+indicators_2+"指标走势";
|
||||
function Draw(records){
|
||||
var strState = "";
|
||||
var fcolor = "";
|
||||
var msg = "";
|
||||
while(!records || records.length < 5){
|
||||
records = exchange.GetRecords();
|
||||
//LogStatus("获取K线中...records.length:",records === null ? "records is null" : records.length);
|
||||
Sleep(Interval);
|
||||
}
|
||||
if(isFirst === true){
|
||||
chart.reset();
|
||||
isFirst = false;
|
||||
preRecordTime = records[records.length - 1].Time;
|
||||
}
|
||||
if(preRecordTime === records[records.length - 1].Time){
|
||||
chart.add([0,[records[records.length - 1].Time,records[records.length - 1].Open,records[records.length - 1].High,records[records.length - 1].Low,records[records.length - 1].Close ],-1]);
|
||||
}else{
|
||||
//更新前一柱
|
||||
chart.add([0,[records[records.length - 2].Time,records[records.length - 2].Open,records[records.length - 2].High,records[records.length - 2].Low,records[records.length - 2].Close ],-1]);
|
||||
|
||||
chart.add([0,[records[records.length - 1].Time,records[records.length - 1].Open,records[records.length - 1].High,records[records.length - 1].Low,records[records.length - 1].Close ]]);
|
||||
|
||||
preRecordTime = records[records.length - 1].Time;
|
||||
}
|
||||
//chart.update(ChartObj); //测试取消
|
||||
//chart.reset(500); //测试取消
|
||||
}
|
||||
function SignOP(time,price,amount,state,message){
|
||||
var msg = "";
|
||||
var fcolor = ""; // ceshi
|
||||
var strState = "";//ceshi
|
||||
msg = "均价:"+price+"币数:"+amount;
|
||||
switch(state){
|
||||
case 3:strState = "自定义信息";fcolor = "black";msg = message;break;
|
||||
case 1:strState = "开多仓";fcolor = "red";break;
|
||||
case 2:strState = "开空仓";fcolor = "green";break;
|
||||
case 0:strState = "平仓";fcolor = "blue";break;
|
||||
}
|
||||
chart.add(1, {x:time, color: fcolor , shape: 'flag', title: strState, text: msg});
|
||||
}
|
||||
//----------------------------------图表模块over------------------------------------------------------------
|
||||
//----------------------------------状态栏表格模块-----------------------------------------------------------
|
||||
var TV = null; //表格对象,控制表格内容。
|
||||
var objTable = null;//用于显示表格的对象。
|
||||
function CreateObjectString(cols,rows){
|
||||
var i = cols;// column 列
|
||||
var j = rows;// row 行
|
||||
var firstCols = 'a';
|
||||
var charValue = 0;
|
||||
var firstName = ""; //对象字符串成员的字母部分
|
||||
var lastName = ""; //对象字符串成员的数字部分
|
||||
var strMember = ""; //对象字符串成员
|
||||
var objStr = "";//返回的字符串
|
||||
if(i > 26){
|
||||
throw "ERROR column must less 26";
|
||||
}
|
||||
var strHead = '{';
|
||||
var strEnd = '}';
|
||||
for(var n = 0 ; n < j; n++){
|
||||
//处理行
|
||||
for(var m = 0 ; m < i; m++){
|
||||
//处理列 处理 abc , 数字=n
|
||||
charValue = firstCols.charCodeAt();//取字符的编码
|
||||
firstName = String.fromCharCode(charValue + m);
|
||||
lastName = n;
|
||||
if(n === j - 1 && m === i - 1 ){
|
||||
strMember = '"' + firstName + lastName + '"' + ':' + '""';
|
||||
}else{
|
||||
strMember = '"' + firstName + lastName + '"' + ':' + '""' + ',';
|
||||
}
|
||||
objStr += strMember;
|
||||
}
|
||||
}
|
||||
objStr = strHead + objStr + strEnd;
|
||||
return objStr;
|
||||
}
|
||||
|
||||
var g_cols = 0;
|
||||
var g_rows = 0;
|
||||
$.TableInit = function(cols,rows){
|
||||
g_cols = cols;
|
||||
g_rows = rows;
|
||||
var str = CreateObjectString(cols,rows);//生成 TV对象 字符串
|
||||
TV = JSON.parse(str); // 解析字符串生成 TV对象
|
||||
var tableString = CreateTableString(cols,rows);//生成表格对象字符串
|
||||
objTable = JSON.parse(tableString);//解析表格对象字符串
|
||||
LogStatus("当前时间:" + (new Date()) + "\n" + "`" + JSON.stringify(objTable) + "`");//初次显示 表格对象到 状态栏
|
||||
//ConnectDate(cols,rows);//初次链接数据
|
||||
return TV;//返回 TV对象
|
||||
};
|
||||
function ConnectDate(cols,rows){//关联函数
|
||||
//处理cols
|
||||
var i = 0;//控制 objTable.cols
|
||||
for(var unit1 in TV){
|
||||
if(i < cols){
|
||||
objTable.cols[i] = TV[unit1];
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
//处理rows
|
||||
var m = 0;//m控制 第几行
|
||||
var n = 0;//n控制 第几个
|
||||
var o = 1;//跳过cols 部分的计数
|
||||
for(var unit2 in TV){
|
||||
if( o <= cols){
|
||||
o++;
|
||||
continue;
|
||||
}
|
||||
if(n >= cols){
|
||||
n = 0;
|
||||
m++;
|
||||
}
|
||||
objTable.rows[m][n] = TV[unit2];
|
||||
n++;
|
||||
}
|
||||
}
|
||||
function CreateTableString(cols,rows){
|
||||
var strHead = '{';
|
||||
var strEnd = '}';
|
||||
var srtTable_type = ' "type": "table",';
|
||||
var strTable_title = ' "title": "运行信息",';
|
||||
var strTable_cols_begin = ' "cols" : [';
|
||||
var strTable_cols_end = '],';
|
||||
var strTable_rows_begin = ' "rows" : [';
|
||||
var strTable_rows_end = ']';
|
||||
var strCols = "";
|
||||
|
||||
var length = 0;
|
||||
for(var y in TV){// 获得 TV对象的 成员个数
|
||||
length++;
|
||||
}
|
||||
|
||||
var i = 1;
|
||||
for(var x in TV){// 初始化 strCols
|
||||
if(i >= cols){
|
||||
strCols += '"' + "TV." + x + '"' ;
|
||||
break;
|
||||
}else{
|
||||
strCols += '"' + "TV." + x + '"' + ',';
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 1;//控制循环 一行cols 后重置的计数
|
||||
var n = 1; //用于最后一次 不加 都好的 计数
|
||||
var m = 1; //用于跳过cols 部分的计数
|
||||
var strRowsUnit = "";
|
||||
var strRows = "";
|
||||
length = length - cols;
|
||||
for(var z in TV){
|
||||
if(m <= cols){//跳过 表格cols 部分
|
||||
m++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(i >= cols){
|
||||
strRowsUnit += '"' + "TV." + z + '"' ;
|
||||
i = 1;//重置i
|
||||
if(n < length){
|
||||
strRowsUnit = '[' + strRowsUnit + ']' + ',';
|
||||
}else if(n === length){
|
||||
strRowsUnit = '[' + strRowsUnit + ']';
|
||||
}
|
||||
strRows += strRowsUnit;
|
||||
strRowsUnit = "";//重置
|
||||
}else{
|
||||
strRowsUnit += '"' + "TV." + z + '"' + ',';
|
||||
i++;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
var tableString = strHead + srtTable_type + strTable_title + strTable_cols_begin + strCols + strTable_cols_end + strTable_rows_begin + strRows + strTable_rows_end + strEnd;
|
||||
return tableString;
|
||||
}
|
||||
$.UpDateLogStatus = function(msg) { //更新状态栏
|
||||
//列用ABC表示,行用0123表示
|
||||
ConnectDate(g_cols,g_rows);//链接数据
|
||||
LogStatus("当前时间:" + (new Date()) + "msg:" + msg + "\n" + "`" + JSON.stringify(objTable) + "`");//更新状态栏
|
||||
};
|
||||
//----------------------------------状态栏表格模块over-------------------------------------------------------
|
||||
//----------------------------------导出函数----------------------------------------------------------------
|
||||
$.SignOP = function(time,price,amount,state,message){//该函数作用是在策略运行时,在K线图表上标记“开多仓”,“开空仓”,“平仓” 的位置。
|
||||
//参数time: 在策略中使用此函数的时间,一般用(new Date()).getTime() , price:这个参数是在标签上显示 价格(多空平), amount:这个参数是在标签上显示 数量(成交) ,state: 这个参数是用来控制标记的类型,state = 1开多 ,2开空 ,0 平仓
|
||||
if(arguments.length < 4){
|
||||
Log("SignOP 函数 必须传入4个参数 : time、price、amount、state");
|
||||
return;
|
||||
}
|
||||
if(typeof(message) === "undefined"){
|
||||
message = "";
|
||||
}
|
||||
SignOP(time,price,amount,state,message);
|
||||
};
|
||||
$.Draw = function(records){
|
||||
Draw(records);
|
||||
};
|
||||
$.AddZhiBiao = function(zhibiao_Array,records,index){//该函数是在图表上添加指标线,zhibiao_Array:这个参数是指标数据(数组),records:产生指标数据的 原始K线数据,index:指标线的编号,从1开始递增
|
||||
if(records[records.length - 1].Time === lastRecordsTime){
|
||||
chart.add([index+1,[records[records.length - 1].Time,zhibiao_Array[zhibiao_Array.length - 1]],-1]);
|
||||
}else{
|
||||
chart.add([index+1,[records[records.length - 2].Time,zhibiao_Array[zhibiao_Array.length - 2]],-1]);
|
||||
chart.add([index+1,[records[records.length - 1].Time,zhibiao_Array[zhibiao_Array.length - 1]]]);
|
||||
//lastRecordsTime = records[records.length - 1].Time; //测试 取消
|
||||
}
|
||||
//chart.update(ChartObj); //测试取消
|
||||
};
|
||||
$.UpDateChart = function(records){//更新图表,每次添加指标线,添加标签 后需要更新 才有效。 records: K线原始数据
|
||||
if(records[records.length - 1].Time !== lastRecordsTime){
|
||||
lastRecordsTime = records[records.length - 1].Time;
|
||||
}
|
||||
chart.update(ChartObj);
|
||||
chart.reset(500);//默认 保留500个K线
|
||||
};
|
||||
//----------------------------------导出函数over-----------------------------------------------------------
|
||||
//测试
|
||||
function main(){
|
||||
///*测试图表功能
|
||||
var i = 0;
|
||||
var records = exchange.GetRecords();
|
||||
while(!records || records.length < 5){
|
||||
records = exchange.GetRecords();
|
||||
Sleep(500);
|
||||
}
|
||||
var zhibiao = [1,2,3,5,6,4,1,21,5];//ceshi
|
||||
var zhibiao2 = [11,22,44,57,8,77,5];
|
||||
|
||||
//$.SignOP((new Date()).getTime(),null,null,3,"自定义信息标记到图表上");// 测试标记 自定义信息 到图表上
|
||||
while(i < 50000){
|
||||
Draw(records);
|
||||
if(i===2000){
|
||||
//Sleep(60*60*1000);
|
||||
SignOP((new Date()).getTime(),2900,1,1);
|
||||
$.SignOP((new Date()).getTime(),null,null,3,"自定义信息标记到图表上");// 测试标记 自定义信息 到图表上
|
||||
}
|
||||
//zhibiao.shift();
|
||||
//zhibiao.push(zhibiao[zhibiao.length - 1] + 1);//ceshi
|
||||
//Log("ceshi"); //ceshi
|
||||
$.AddZhiBiao(zhibiao,records,1);
|
||||
//$.AddZhiBiao(zhibiao2,records,2);
|
||||
//Log("ceshi"); //ceshi
|
||||
|
||||
//Draw(records);
|
||||
//Log("ceshi1"); //ceshi
|
||||
Sleep(20000);
|
||||
records = exchange.GetRecords();
|
||||
$.UpDateChart(records);//更新图表
|
||||
i++;
|
||||
}
|
||||
//*/
|
||||
/*测试状态栏表格功能*/
|
||||
var cols = 6;//列 设置一个变量 代表 列
|
||||
var rows = 4;//行 设置一个变量 代表 行
|
||||
$.TableInit(cols,rows); //初始化 状态栏 会显示 各个单元格的 坐标
|
||||
///*
|
||||
for(var x in TV){
|
||||
TV[x] = "lalala";// 全部单元格写成 lalala
|
||||
}
|
||||
//更新表格 显示 lalala, 表头 数据不能重复 ,否则显示不出来。
|
||||
/*
|
||||
TV.a0 = "a0";
|
||||
TV.b0 = "b0";
|
||||
TV.c0 = "c0";
|
||||
TV.d0 = "d0";
|
||||
TV.e0 = "e0";
|
||||
TV.f0 = "f0";//先把 表头数据写成不一样的
|
||||
*/
|
||||
$.UpDateLogStatus(cols,rows);//更新 状态栏表格
|
||||
|
||||
///*
|
||||
//怎么在 表格里面写入数据呢?
|
||||
var num = 100;
|
||||
var text = "文本:测试表格文本";
|
||||
var obj = {name:"对象",age:"19",sex:"girl"};
|
||||
var array = ["数组",22,33,54];
|
||||
TV.a1 = num;
|
||||
TV.c2 = text;
|
||||
TV.b3 = obj;
|
||||
TV.b0 = array;
|
||||
|
||||
$.UpDateLogStatus(cols,rows);//再次更新 状态栏表格
|
||||
//*/
|
||||
}
|
||||
/*修改
|
||||
1、增加了是否开启右边坐标轴
|
||||
2、增加参数 lineType : spline / line
|
||||
*/
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/29718
|
||||
策略名称: 均线策略 30 行搞定 (复制)
|
||||
策略作者: pangshe
|
||||
策略描述:
|
||||
|
||||
"Talk is cheap. Show me the code"
|
||||
|
||||
教学性质, 实盘慎用.
|
||||
|
||||
|
||||
注: ` 策略使用了交易模板类库`
|
||||
|
||||
`希望新手从此策略入门, 一步步学习编写策略, 并体验到模拟与真实环境对交易系统的影响`
|
||||
|
||||
https://dn-filebox.qbox.me/fb4d0c7d773ca83e9d0230927705d419dc0bbeaa.png
|
||||
|
||||
*/
|
||||
|
||||
function main() {
|
||||
var STATE_IDLE = -1;
|
||||
var state = STATE_IDLE;
|
||||
var initAccount = $.GetAccount();
|
||||
|
||||
|
||||
Sleep(Interval*1000);
|
||||
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
策略出处: 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);
|
||||
}
|
||||
}
|
||||
@@ -178,8 +178,8 @@ $.PlotLine = function(label, dot, time) {
|
||||
}
|
||||
var seriesIdx = labelIdx[label]
|
||||
if (typeof(seriesIdx) === 'undefined') {
|
||||
preDotTime[seriesIdx] = 0
|
||||
seriesIdx = series.length
|
||||
preDotTime[seriesIdx] = 0
|
||||
labelIdx[label] = seriesIdx;
|
||||
series.push({
|
||||
type: 'line',
|
||||
|
||||
+609
@@ -0,0 +1,609 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/33431
|
||||
策略名称: 策略框架模板 (复制)
|
||||
策略作者: zhongguo888
|
||||
策略描述:
|
||||
|
||||
新策略框架
|
||||
现货, 开多,开空,止损,加仓,平仓。
|
||||
// - 状态
|
||||
/*
|
||||
var TASK_IDLE = 0;
|
||||
var TASK_OPEN_LONG = 1;
|
||||
var TASK_OPEN_SHORT = 2;
|
||||
var TASK_ADD = 3;
|
||||
var TASK_ST = 4;
|
||||
var TASK_COVER = 5;
|
||||
*/
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
---------- ----- -----------
|
||||
OpMode 0 下单方式: 吃单|挂单
|
||||
MaxSpace 0.5 挂单失效距离
|
||||
SlidePrice 0.1 下单滑动价(元)
|
||||
MaxAmount 0.8 开仓最大单次下单量
|
||||
RetryDelay 500 失败重试(毫秒)
|
||||
Interval 500 轮询间隔
|
||||
*/
|
||||
|
||||
// 模板全局变量
|
||||
// - 状态
|
||||
var TASK_IDLE = 0;
|
||||
var TASK_OPEN_LONG = 1;
|
||||
var TASK_OPEN_SHORT = 2;
|
||||
var TASK_ADD = 3;
|
||||
var TASK_ST = 4;
|
||||
var TASK_COVER = 5;
|
||||
// - 变量
|
||||
var Tasks = [];
|
||||
var IDLE = 11;
|
||||
var LONG = 22;
|
||||
var SHORT = 33;
|
||||
var cmdList = [TASK_IDLE, TASK_OPEN_LONG, TASK_OPEN_SHORT, TASK_ADD, TASK_ST, TASK_COVER];
|
||||
|
||||
// 现货买卖功能函数
|
||||
function CancelPendingOrders(e, orderType) {
|
||||
while (true) {
|
||||
var orders = e.GetOrders();
|
||||
if (!orders) {
|
||||
Sleep(RetryDelay);
|
||||
continue;
|
||||
}
|
||||
var processed = 0;
|
||||
for (var j = 0; j < orders.length; j++) {
|
||||
if (typeof(orderType) === 'number' && orders[j].Type !== orderType) {
|
||||
continue;
|
||||
}
|
||||
e.CancelOrder(orders[j].Id, orders[j]);
|
||||
processed++;
|
||||
if (j < (orders.length - 1)) {
|
||||
Sleep(RetryDelay);
|
||||
}
|
||||
}
|
||||
if (processed === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function GetAccount(e, waitFrozen) {
|
||||
if (typeof(waitFrozen) == 'undefined') {
|
||||
waitFrozen = false;
|
||||
}
|
||||
var account = null;
|
||||
var alreadyAlert = false;
|
||||
while (true) {
|
||||
account = _C(e.GetAccount);
|
||||
if (!waitFrozen || (account.FrozenStocks < e.GetMinStock() && account.FrozenBalance < 0.01)) {
|
||||
break;
|
||||
}
|
||||
if (!alreadyAlert) {
|
||||
alreadyAlert = true;
|
||||
Log("发现账户有冻结的钱或币", account);
|
||||
}
|
||||
Sleep(RetryDelay);
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
function StripOrders(e, orderId) {
|
||||
var order = null;
|
||||
if (typeof(orderId) == 'undefined') {
|
||||
orderId = null;
|
||||
}
|
||||
while (true) {
|
||||
var dropped = 0;
|
||||
var orders = _C(e.GetOrders);
|
||||
for (var i = 0; i < orders.length; i++) {
|
||||
if (orders[i].Id == orderId) {
|
||||
order = orders[i];
|
||||
} else {
|
||||
var extra = "";
|
||||
if (orders[i].DealAmount > 0) {
|
||||
extra = "成交: " + orders[i].DealAmount;
|
||||
} else {
|
||||
extra = "未成交";
|
||||
}
|
||||
e.CancelOrder(orders[i].Id, orders[i].Type == ORDER_TYPE_BUY ? "买单" : "卖单", extra);
|
||||
dropped++;
|
||||
}
|
||||
}
|
||||
if (dropped === 0) {
|
||||
break;
|
||||
}
|
||||
Sleep(RetryDelay);
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
// mode = 0 : direct buy, 1 : buy as buy1
|
||||
function Trade(e, tradeType, tradeAmount, mode, slidePrice, maxAmount, maxSpace, retryDelay) {
|
||||
var initAccount = GetAccount(e, true);
|
||||
var nowAccount = initAccount;
|
||||
var orderId = null;
|
||||
var prePrice = 0;
|
||||
var dealAmount = 0;
|
||||
var diffMoney = 0;
|
||||
var isFirst = true;
|
||||
var tradeFunc = tradeType == ORDER_TYPE_BUY ? e.Buy : e.Sell;
|
||||
var isBuy = tradeType == ORDER_TYPE_BUY;
|
||||
while (true) {
|
||||
var ticker = _C(e.GetTicker);
|
||||
var tradePrice = 0;
|
||||
if (isBuy) {
|
||||
tradePrice = _N((mode === 0 ? ticker.Sell : ticker.Buy) + slidePrice, 4);
|
||||
} else {
|
||||
tradePrice = _N((mode === 0 ? ticker.Buy : ticker.Sell) - slidePrice, 4);
|
||||
}
|
||||
if (!orderId) {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
nowAccount = GetAccount(e, true);
|
||||
}
|
||||
var doAmount = 0;
|
||||
if (isBuy) {
|
||||
diffMoney = _N(initAccount.Balance - nowAccount.Balance, 4);
|
||||
dealAmount = _N(nowAccount.Stocks - initAccount.Stocks, 4);
|
||||
doAmount = Math.min(maxAmount, tradeAmount - dealAmount, _N((nowAccount.Balance - 10) / tradePrice, 4));
|
||||
} else {
|
||||
diffMoney = _N(nowAccount.Balance - initAccount.Balance, 4);
|
||||
dealAmount = _N(initAccount.Stocks - nowAccount.Stocks, 4);
|
||||
doAmount = Math.min(maxAmount, tradeAmount - dealAmount, nowAccount.Stocks);
|
||||
}
|
||||
if (doAmount < e.GetMinStock()) {
|
||||
break;
|
||||
}
|
||||
prePrice = tradePrice;
|
||||
orderId = tradeFunc(tradePrice, doAmount, ticker);
|
||||
if (!orderId) {
|
||||
CancelPendingOrders(e, tradeType);
|
||||
}
|
||||
} else {
|
||||
if (mode === 0 || (Math.abs(tradePrice - prePrice) > maxSpace)) {
|
||||
orderId = null;
|
||||
}
|
||||
var order = StripOrders(e, orderId);
|
||||
if (!order) {
|
||||
orderId = null;
|
||||
}
|
||||
}
|
||||
Sleep(retryDelay);
|
||||
}
|
||||
|
||||
if (dealAmount <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
price: _N(diffMoney / dealAmount, 4),
|
||||
amount: dealAmount
|
||||
};
|
||||
}
|
||||
|
||||
var BuySpot = function(e, amount) {
|
||||
if (typeof(e) === 'number') {
|
||||
amount = e;
|
||||
e = exchange;
|
||||
}
|
||||
return Trade(e, ORDER_TYPE_BUY, amount, OpMode, SlidePrice, MaxAmount, MaxSpace, RetryDelay);
|
||||
};
|
||||
|
||||
var SellSpot = function(e, amount) {
|
||||
if (typeof(e) === 'number') {
|
||||
amount = e;
|
||||
e = exchange;
|
||||
}
|
||||
return Trade(e, ORDER_TYPE_SELL, amount, OpMode, SlidePrice, MaxAmount, MaxSpace, RetryDelay);
|
||||
};
|
||||
|
||||
// 模板内功能函数
|
||||
// - 初始化函数 tasksInit
|
||||
function TasksInit(tasks){
|
||||
_.each(tasks, function(task){ // OpenLongAmount OpenShortAmount AddAmount StopLossAmount STATE minStock initAccount Currency Name Label nowAccount lastAccount lastTASKSTATE Profit floatProfit lastPrice preProfit NowPositionInfo
|
||||
task.OpenLongAmount = 0;
|
||||
task.OpenShortAmount = 0;
|
||||
task.AddAmount = 0;
|
||||
task.StopLossAmount = 0;
|
||||
task.STATE = IDLE;
|
||||
task.minStock = _C(task.Exchange.GetMinStock);
|
||||
task.initAccount = _C(task.Exchange.GetAccount);
|
||||
task.Currency = _C(task.Exchange.GetCurrency);
|
||||
task.Name = _C(task.Exchange.GetName);
|
||||
task.Label = _C(task.Exchange.GetLabel);
|
||||
task.nowAccount = task.initAccount;
|
||||
task.lastAccount = task.initAccount;
|
||||
task.lastTASKSTATE = TASK_IDLE;
|
||||
task.Profit = 0;
|
||||
task.floatProfit = 0;
|
||||
task.lastPrice = 0;
|
||||
task.preProfit = 0;
|
||||
task.CMD = TASK_IDLE;
|
||||
task.CMD_AMOUNT = 0;
|
||||
task.NowPositionInfo = {
|
||||
avgPrice: 0, // 持仓均价
|
||||
amount: 0 , // 持仓量
|
||||
floatProfit: 0 // 浮动盈亏
|
||||
};
|
||||
Log(task.Name, task.Label, "初始账户信息:", task.initAccount);
|
||||
});
|
||||
}
|
||||
|
||||
function TranslateSTATE_TASKSTATE(STATE){
|
||||
switch(STATE){
|
||||
case 0: //TASK_IDLE
|
||||
return "无任务";
|
||||
case 1: //TASK_OPEN_LONG
|
||||
return "建多仓任务";
|
||||
case 2: //TASK_OPEN_SHORT
|
||||
return "建空仓任务";
|
||||
case 3: //TASK_ADD
|
||||
return "加仓任务";
|
||||
case 4: //TASK_ST
|
||||
return "止损任务";
|
||||
case 5: //TASK_COVER
|
||||
return "平仓任务";
|
||||
case 11: //IDLE
|
||||
return "未开仓";
|
||||
case 22: //LONG
|
||||
return "持多仓";
|
||||
case 33: //SHORT
|
||||
return "持空仓";
|
||||
}
|
||||
}
|
||||
|
||||
// - 开多仓
|
||||
function OpenLong(task){
|
||||
// Log("Buy", task.OpenLongAmount, task.Exchange.GetName(), task.Indication); // ceshi
|
||||
var BuyInfo = BuySpot(task.Exchange, task.OpenLongAmount);
|
||||
if(!BuyInfo){
|
||||
//Log("function BuySpot return :", BuyInfo);
|
||||
}else{
|
||||
task.NowPositionInfo.amount = BuyInfo.amount;
|
||||
task.NowPositionInfo.avgPrice = BuyInfo.price;
|
||||
task.STATE = LONG;
|
||||
task.nowAccount = _C(task.Exchange.GetAccount); // update nowAccount
|
||||
}
|
||||
}
|
||||
|
||||
// - 开空仓
|
||||
function OpenShort(task){
|
||||
var SellInfo = SellSpot(task.Exchange, task.OpenShortAmount);
|
||||
if(!SellInfo){
|
||||
//Log("function SellSpot return :", SellInfo);
|
||||
}else{
|
||||
task.NowPositionInfo.amount = SellInfo.amount;
|
||||
task.NowPositionInfo.avgPrice = SellInfo.price;
|
||||
task.STATE = SHORT;
|
||||
task.nowAccount = _C(task.Exchange.GetAccount); // update nowAccount
|
||||
}
|
||||
}
|
||||
// - 加仓
|
||||
function AddPosition(task){
|
||||
var tradeInfo = null;
|
||||
if(task.STATE === LONG){
|
||||
tradeInfo = BuySpot(task.Exchange, task.AddAmount);
|
||||
}else if(task.STATE === SHORT){
|
||||
tradeInfo = SellSpot(task.Exchange, task.AddAmount);
|
||||
}
|
||||
if(!tradeInfo){
|
||||
//Log("function return :", tradeInfo);
|
||||
}else{
|
||||
task.NowPositionInfo.avgPrice = (tradeInfo.price * tradeInfo.amount + task.NowPositionInfo.avgPrice * task.NowPositionInfo.amount) / (tradeInfo.amount + task.NowPositionInfo.amount);
|
||||
task.NowPositionInfo.amount += tradeInfo.amount;
|
||||
task.nowAccount = _C(task.Exchange.GetAccount); // update nowAccount
|
||||
}
|
||||
}
|
||||
// - 止损
|
||||
function StopLoss(task){
|
||||
var tradeInfo = null;
|
||||
task.StopLossAmount = Math.min(task.StopLossAmount, task.NowPositionInfo.amount);
|
||||
if(task.STATE === LONG){
|
||||
tradeInfo = SellSpot(task.Exchange, task.StopLossAmount);
|
||||
}else if(task.STATE === SHORT){
|
||||
tradeInfo = BuySpot(task.Exchange, task.StopLossAmount);
|
||||
}
|
||||
if(!tradeInfo){
|
||||
//Log("function return :", tradeInfo);
|
||||
}else if(Math.abs(task.NowPositionInfo.amount - tradeInfo.amount) > task.minStock){
|
||||
task.NowPositionInfo.amount -= tradeInfo.amount;
|
||||
Log("未完全平仓,剩余:", Math.abs(task.NowPositionInfo.amount - tradeInfo.amount), " tradeInfo:", tradeInfo, "NowPositionInfo:", task.NowPositionInfo);
|
||||
task.nowAccount = _C(task.Exchange.GetAccount); // update nowAccount
|
||||
}else{
|
||||
task.NowPositionInfo.amount = 0;
|
||||
task.NowPositionInfo.avgPrice = 0;
|
||||
task.STATE = IDLE;
|
||||
CalProfit(task); // equal Cover
|
||||
}
|
||||
}
|
||||
// - 平仓
|
||||
function Cover(task){
|
||||
var tradeInfo = null;
|
||||
if(task.STATE === LONG){
|
||||
tradeInfo = SellSpot(task.Exchange, task.NowPositionInfo.amount);
|
||||
}else if(task.STATE === SHORT){
|
||||
tradeInfo = BuySpot(task.Exchange, task.NowPositionInfo.amount);
|
||||
}
|
||||
if(!tradeInfo){
|
||||
//Log("function return :", tradeInfo);
|
||||
}else if(Math.abs(task.NowPositionInfo.amount - tradeInfo.amount) > task.minStock){
|
||||
task.NowPositionInfo.amount -= tradeInfo.amount;
|
||||
Log("未完全平仓,剩余:", Math.abs(task.NowPositionInfo.amount - tradeInfo.amount), " tradeInfo:", tradeInfo, "NowPositionInfo:", task.NowPositionInfo);
|
||||
}else{
|
||||
task.NowPositionInfo.amount = 0;
|
||||
task.NowPositionInfo.avgPrice = 0;
|
||||
task.STATE = IDLE;
|
||||
CalProfit(task);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算盈亏
|
||||
var SumProfit = 0;
|
||||
function CalProfit(task){
|
||||
//task.lastAccount = task.nowAccount;
|
||||
task.nowAccount = _C(task.Exchange.GetAccount);
|
||||
task.lastAccount = task.nowAccount;
|
||||
var Bdiff = task.nowAccount.Balance - task.initAccount.Balance;
|
||||
task.preProfit = task.Profit;
|
||||
task.Profit = Bdiff;
|
||||
|
||||
SumProfit = 0;
|
||||
_.each(Tasks, function(task){
|
||||
SumProfit += task.Profit;
|
||||
});
|
||||
LogProfit(SumProfit, task.Name + "-" + task.Currency + "-" + task.Label + "- 本次:" + (task.Profit - task.preProfit));
|
||||
}
|
||||
|
||||
function CalFloatProfit(task){
|
||||
var diffPrice = 0;
|
||||
if(task.STATE === LONG){
|
||||
diffPrice = (task.lastPrice - task.NowPositionInfo.avgPrice);
|
||||
task.NowPositionInfo.floatProfit = _N(diffPrice * task.NowPositionInfo.amount);
|
||||
}else if(task.STATE === SHORT){
|
||||
diffPrice = (task.NowPositionInfo.avgPrice - task.lastPrice);
|
||||
task.NowPositionInfo.floatProfit = _N(diffPrice * task.NowPositionInfo.amount);
|
||||
}
|
||||
}
|
||||
|
||||
function isInCMD_List(cmd){
|
||||
for(var i = 0; i < cmdList.length ; i++){
|
||||
if(cmd === cmdList[i]){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function CMD(index, CMD_STR, amount){
|
||||
if(index < Tasks.length || typeof(amount) === 'undefined' || isInCMD_List(CMD_STR) === false){
|
||||
Tasks[index].CMD = CMD_STR;
|
||||
Tasks[index].CMD_AMOUNT = amount;
|
||||
}else{
|
||||
Log("错误:", "index:" + index, "CMD_STR:" + CMD_STR, "amount:" + amount);
|
||||
}
|
||||
}
|
||||
|
||||
$.TaskCmd = function(cmd, amount, lastPrice){
|
||||
if(cmd === TASK_IDLE && typeof(amount) === 'undefined'){
|
||||
amount = 0;
|
||||
}else if((cmd === TASK_ST || cmd === TASK_COVER) && typeof(amount) === 'undefined'){
|
||||
amount = 0;
|
||||
}else if(typeof(amount) === 'undefined'){
|
||||
throw "没有传入要操作下单量!";
|
||||
}
|
||||
if(typeof(lastPrice) === 'undefined'){
|
||||
return {cmd: cmd, amount: amount, lastPrice: -1};
|
||||
}else{
|
||||
return {cmd: cmd, amount: amount, lastPrice: lastPrice};
|
||||
}
|
||||
}
|
||||
|
||||
// 模板导出函数
|
||||
var usedTime = 0;
|
||||
$.Trend = function() {
|
||||
TasksInit(Tasks);
|
||||
while(true){
|
||||
var beginTime = new Date().getTime();
|
||||
_.each(Tasks, function(task){
|
||||
task.OpenLongAmount = 0;
|
||||
task.OpenShortAmount = 0;
|
||||
task.AddAmount = 0;
|
||||
task.StopLossAmount = 0;
|
||||
});
|
||||
_.each(Tasks, function(task){
|
||||
var obj = task.onTick();
|
||||
//Log("obj:", obj);
|
||||
var ret = obj.cmd;
|
||||
var amount = obj.amount;
|
||||
if(obj.lastPrice !== -1){
|
||||
task.lastPrice = obj.lastPrice;
|
||||
}else{
|
||||
task.lastPrice = (_C(task.Exchange.GetTicker)).Last;
|
||||
}
|
||||
if(task.CMD !== TASK_IDLE){
|
||||
ret = task.CMD;
|
||||
amount = task.CMD_AMOUNT;
|
||||
}
|
||||
switch (ret) {
|
||||
// switch status..
|
||||
case TASK_OPEN_LONG: // 开多仓
|
||||
if(task.STATE === IDLE){
|
||||
task.lastTASKSTATE = TASK_OPEN_LONG;
|
||||
task.OpenLongAmount = amount;
|
||||
OpenLong(task);
|
||||
}else{
|
||||
//Log("TASK_OPEN_LONG 当前状态为:", task.STATE, "!= ", IDLE);
|
||||
}
|
||||
break;
|
||||
case TASK_OPEN_SHORT: // 开空仓
|
||||
if(task.STATE === IDLE){
|
||||
task.lastTASKSTATE = TASK_OPEN_SHORT;
|
||||
task.OpenShortAmount = amount;
|
||||
OpenShort(task);
|
||||
}else{
|
||||
//Log("TASK_OPEN_SHORT 当前状态为:", task.STATE, "!= ", IDLE);
|
||||
}
|
||||
break;
|
||||
case TASK_ADD: // 加仓
|
||||
if(task.STATE === LONG || task.STATE === SHORT){
|
||||
task.lastTASKSTATE = TASK_ADD;
|
||||
task.AddAmount = amount;
|
||||
AddPosition(task);
|
||||
}else{
|
||||
//Log("TASK_ADD 当前状态为:", task.STATE, "!=", LONG, "或者", SHORT);
|
||||
}
|
||||
break;
|
||||
case TASK_ST: // 止损
|
||||
if(task.STATE === LONG || task.STATE === SHORT){
|
||||
task.lastTASKSTATE = TASK_ST;
|
||||
task.StopLossAmount = amount;
|
||||
StopLoss(task);
|
||||
}else{
|
||||
//Log("TASK_ST 当前状态为:", task.STATE, "!=", LONG, "或者", SHORT);
|
||||
}
|
||||
break;
|
||||
case TASK_COVER: // 平仓
|
||||
if(task.STATE === LONG || task.STATE === SHORT){
|
||||
task.lastTASKSTATE = TASK_COVER;
|
||||
Cover(task);
|
||||
}else{
|
||||
//Log("TASK_COVER 当前状态为:", task.STATE, "!= ", LONG, "或者", SHORT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// 计算浮动收益
|
||||
CalFloatProfit(task);
|
||||
|
||||
// 重置CMD
|
||||
if(task.CMD !== TASK_IDLE){
|
||||
task.CMD = TASK_IDLE;
|
||||
task.CMD_AMOUNT = 0;
|
||||
}
|
||||
});
|
||||
|
||||
// 交互模块
|
||||
var cmd = GetCommand(); // 调用API 获取界面交互控件的消息。
|
||||
if (cmd) { // 判断是否有消息
|
||||
var js = cmd.split(':', 2)[1]; // 分割 返回的消息 字符串, 限制返回2个, 把索引为1的 元素 赋值给 名为js 的变量
|
||||
Log("执行代码:", js); // 输出 执行的代码
|
||||
try { // 异常检测
|
||||
eval(js); // 执行 eval函数, 该函数执行传入的参数(代码)。
|
||||
} catch (e) { // 抛出异常
|
||||
Log("交互代码错误: Exception", e); // 输出错误信息
|
||||
}
|
||||
}
|
||||
|
||||
// 显示在状态栏
|
||||
var endTime = new Date().getTime();
|
||||
usedTime = endTime - beginTime;
|
||||
$.ToTable();
|
||||
Sleep(Interval);
|
||||
}
|
||||
}
|
||||
|
||||
$.Relation_Exchange_onTick = function(Exchange, onTick){
|
||||
var task = {
|
||||
onTick : onTick,
|
||||
Exchange : Exchange
|
||||
};
|
||||
Tasks.push(task);
|
||||
};
|
||||
|
||||
$.ToTable = function(){
|
||||
var tables = [];
|
||||
_.each(Tasks, function(task){
|
||||
var table = {type: "table", title: task.Exchange.GetName() + task.Exchange.GetLabel(), cols: ["desc", "value"], rows: []};
|
||||
table.rows.push(["初始账户信息:", task.initAccount]);
|
||||
table.rows.push(["当前账户信息:", task.nowAccount]);
|
||||
table.rows.push(["上次平仓后账户信息:", task.lastAccount]);
|
||||
table.rows.push(["持仓信息:", task.NowPositionInfo]);
|
||||
table.rows.push(["状态:", TranslateSTATE_TASKSTATE(task.STATE)]);
|
||||
table.rows.push(["最近任务:", TranslateSTATE_TASKSTATE(task.lastTASKSTATE)]);
|
||||
table.rows.push(["浮动盈亏:", task.floatProfit = task.NowPositionInfo.floatProfit]);
|
||||
table.rows.push(["总盈亏:", task.Profit]);
|
||||
// 处理自定义
|
||||
// OpenLongAmount OpenShortAmount AddAmount StopLossAmount STATE minStock initAccount Currency Name Label nowAccount
|
||||
// lastAccount lastTASKSTATE Profit floatProfit lastPrice preProfit NowPositionInfo
|
||||
for(var key in task){
|
||||
if(key === 'OpenLongAmount' || key === 'OpenShortAmount' || key === 'AddAmount' || key === 'StopLossAmount' || key === 'STATE' || key === 'minStock' || key === 'initAccount' || key === 'Currency' ||
|
||||
key === 'Name' || key === 'Label' || key === 'nowAccount' || key === 'lastAccount' || key === 'lastTASKSTATE' || key === 'Profit' || key === 'floatProfit' || key === 'lastPrice' || key === 'preProfit' || key === 'NowPositionInfo' ||
|
||||
key === 'onTick' || key === 'Exchange' || key === 'CMD' || key === 'CMD_AMOUNT'){
|
||||
continue;
|
||||
}else{
|
||||
table.rows.push([key, task[key]]);
|
||||
}
|
||||
}
|
||||
|
||||
tables.push(table);
|
||||
});
|
||||
var CMD_STR = "TASK_OPEN_LONG : 开多仓, TASK_OPEN_SHORT : 开空仓, TASK_ADD : 加仓, TASK_ST : 止损, TASK_COVER : 平仓 , 命令函数:CMD(index, CMD_STR, amount)";
|
||||
LogStatus(" time: " + _D() + " 耗时:" + usedTime + "合计收益:" + SumProfit + "命令:" + '\n' + CMD_STR + '\n`' + JSON.stringify(tables) + '`');
|
||||
// var table = {type: "table", title: "", cols: [], row: []};
|
||||
};
|
||||
|
||||
$.AddData = function(index, dataKey, dataValue){
|
||||
Tasks[index][dataKey] = dataValue;
|
||||
};
|
||||
|
||||
// 以下是测试代码
|
||||
/*- 状态 在使用 模板时需要在 主策略内声明
|
||||
var TASK_IDLE = 0;
|
||||
var TASK_OPEN_LONG = 1;
|
||||
var TASK_OPEN_SHORT = 2;
|
||||
var TASK_ADD = 3;
|
||||
var TASK_ST = 4;
|
||||
var TASK_COVER = 5;
|
||||
*/
|
||||
|
||||
function onTick1() {
|
||||
// EMA
|
||||
var records = _C(exchanges[0].GetRecords);
|
||||
if(records.length < 11){
|
||||
return $.TaskCmd(TASK_IDLE);
|
||||
}
|
||||
var ema_fast = TA.MA(records, 7);
|
||||
var ema_slow = TA.MA(records, 20);
|
||||
// $.AddData = function(index, dataKey, dataValue)
|
||||
|
||||
var data = "fast[-2]:" + ema_fast[ema_fast.length - 2] + " slow[-2]" + ema_slow[ema_slow.length - 2] + " fast[-1]:" + ema_fast[ema_fast.length - 1] + " slow[-1]:" + ema_slow[ema_slow.length - 1];
|
||||
$.AddData(0, "MA", data);
|
||||
|
||||
if (ema_fast[ema_fast.length - 1] < ema_slow[ema_slow.length - 1] && ema_fast[ema_fast.length - 2] > ema_slow[ema_slow.length - 2]) {
|
||||
return $.TaskCmd(TASK_COVER);
|
||||
}else if(ema_fast[ema_fast.length - 1] > ema_slow[ema_slow.length - 1] && ema_fast[ema_fast.length - 2] < ema_slow[ema_slow.length - 2]){
|
||||
return $.TaskCmd(TASK_OPEN_LONG, 0.5);
|
||||
}
|
||||
|
||||
return $.TaskCmd(TASK_IDLE);
|
||||
}
|
||||
|
||||
function onTick2() {
|
||||
// MACD
|
||||
var records = _C(exchanges[1].GetRecords);
|
||||
if(records.length < 15){
|
||||
$.TaskCmd(TASK_IDLE);
|
||||
}
|
||||
var macd = TA.MACD(records);
|
||||
var dif = macd[0];
|
||||
var dea = macd[1];
|
||||
|
||||
var data = "dif[-2]:" + dif[dif.length - 2] + " dea[-2]" + dea[dea.length - 2] + " dif[-1]:" + dif[dif.length - 1] + " dea[-1]:" + dea[dea.length - 1];
|
||||
$.AddData(1, "MACD", data);
|
||||
|
||||
if (dif[dif.length - 1] > dea[dea.length - 1] && dif[dif.length - 2] < dea[dea.length - 2]) {
|
||||
//return $.TaskCmd(TASK_OPEN_SHORT, 0.8);
|
||||
return $.TaskCmd(TASK_COVER);
|
||||
}else if(dif[dif.length - 1] < dea[dea.length - 1] && dif[dif.length - 2] > dea[dea.length - 2]){
|
||||
//return $.TaskCmd(TASK_COVER);
|
||||
return $.TaskCmd(TASK_OPEN_LONG, 0.8);
|
||||
}
|
||||
|
||||
return $.TaskCmd(TASK_IDLE);
|
||||
}
|
||||
|
||||
function main() {
|
||||
$.Relation_Exchange_onTick(exchanges[0], onTick1);
|
||||
$.Relation_Exchange_onTick(exchanges[1], onTick2);
|
||||
$.Trend(); // 不用传参数。
|
||||
}
|
||||
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
策略出处: https://www.botvs.com/strategy/30861
|
||||
策略名称: 策略框架模板
|
||||
策略作者: 小小梦
|
||||
策略描述:
|
||||
|
||||
新策略框架
|
||||
现货, 开多,开空,止损,加仓,平仓。
|
||||
// - 状态
|
||||
/*
|
||||
var TASK_IDLE = 0;
|
||||
var TASK_OPEN_LONG = 1;
|
||||
var TASK_OPEN_SHORT = 2;
|
||||
var TASK_ADD = 3;
|
||||
var TASK_ST = 4;
|
||||
var TASK_COVER = 5;
|
||||
*/
|
||||
|
||||
|
||||
参数 默认值 描述
|
||||
---------- ----- -----------
|
||||
OpMode 0 下单方式: 吃单|挂单
|
||||
MaxSpace 0.5 挂单失效距离
|
||||
SlidePrice 0.1 下单滑动价(元)
|
||||
MaxAmount 0.8 开仓最大单次下单量
|
||||
RetryDelay 500 失败重试(毫秒)
|
||||
Interval 500 轮询间隔
|
||||
*/
|
||||
|
||||
// 模板全局变量
|
||||
// - 状态
|
||||
var TASK_IDLE = 0;
|
||||
var TASK_OPEN_LONG = 1;
|
||||
var TASK_OPEN_SHORT = 2;
|
||||
var TASK_ADD = 3;
|
||||
var TASK_ST = 4;
|
||||
var TASK_COVER = 5;
|
||||
// - 变量
|
||||
var Tasks = [];
|
||||
var IDLE = 11;
|
||||
var LONG = 22;
|
||||
var SHORT = 33;
|
||||
var cmdList = [TASK_IDLE, TASK_OPEN_LONG, TASK_OPEN_SHORT, TASK_ADD, TASK_ST, TASK_COVER];
|
||||
// 计算盈亏
|
||||
var SumProfit = 0;
|
||||
|
||||
// 现货买卖功能函数
|
||||
function CancelPendingOrders(e, orderType) {
|
||||
while (true) {
|
||||
var orders = e.GetOrders();
|
||||
if (!orders) {
|
||||
Sleep(RetryDelay);
|
||||
continue;
|
||||
}
|
||||
var processed = 0;
|
||||
for (var j = 0; j < orders.length; j++) {
|
||||
if (typeof(orderType) === 'number' && orders[j].Type !== orderType) {
|
||||
continue;
|
||||
}
|
||||
e.CancelOrder(orders[j].Id, orders[j]);
|
||||
processed++;
|
||||
if (j < (orders.length - 1)) {
|
||||
Sleep(RetryDelay);
|
||||
}
|
||||
}
|
||||
if (processed === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function GetAccount(e, waitFrozen) {
|
||||
if (typeof(waitFrozen) == 'undefined') {
|
||||
waitFrozen = false;
|
||||
}
|
||||
var account = null;
|
||||
var alreadyAlert = false;
|
||||
while (true) {
|
||||
account = _C(e.GetAccount);
|
||||
if (!waitFrozen || (account.FrozenStocks < e.GetMinStock() && account.FrozenBalance < 0.01)) {
|
||||
break;
|
||||
}
|
||||
if (!alreadyAlert) {
|
||||
alreadyAlert = true;
|
||||
Log("发现账户有冻结的钱或币", account);
|
||||
}
|
||||
Sleep(RetryDelay);
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
function StripOrders(e, orderId) {
|
||||
var order = null;
|
||||
if (typeof(orderId) == 'undefined') {
|
||||
orderId = null;
|
||||
}
|
||||
while (true) {
|
||||
var dropped = 0;
|
||||
var orders = _C(e.GetOrders);
|
||||
for (var i = 0; i < orders.length; i++) {
|
||||
if (orders[i].Id == orderId) {
|
||||
order = orders[i];
|
||||
} else {
|
||||
var extra = "";
|
||||
if (orders[i].DealAmount > 0) {
|
||||
extra = "成交: " + orders[i].DealAmount;
|
||||
} else {
|
||||
extra = "未成交";
|
||||
}
|
||||
e.CancelOrder(orders[i].Id, orders[i].Type == ORDER_TYPE_BUY ? "买单" : "卖单", extra);
|
||||
dropped++;
|
||||
}
|
||||
}
|
||||
if (dropped === 0) {
|
||||
break;
|
||||
}
|
||||
Sleep(RetryDelay);
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
function Trade(e, tradeType, tradeAmount, mode, slidePrice, maxAmount, maxSpace, retryDelay) {
|
||||
var initAccount = GetAccount(e, true);
|
||||
var nowAccount = initAccount;
|
||||
var orderId = null;
|
||||
var prePrice = 0;
|
||||
var dealAmount = 0;
|
||||
var diffMoney = 0;
|
||||
var isFirst = true;
|
||||
var tradeFunc = tradeType == ORDER_TYPE_BUY ? e.Buy : e.Sell;
|
||||
var isBuy = tradeType == ORDER_TYPE_BUY;
|
||||
while (true) {
|
||||
var ticker = _C(e.GetTicker);
|
||||
var tradePrice = 0;
|
||||
if (isBuy) {
|
||||
tradePrice = _N((mode === 0 ? ticker.Sell : ticker.Buy) + slidePrice, 4);
|
||||
} else {
|
||||
tradePrice = _N((mode === 0 ? ticker.Buy : ticker.Sell) - slidePrice, 4);
|
||||
}
|
||||
if (!orderId) {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
nowAccount = GetAccount(e, true);
|
||||
}
|
||||
var doAmount = 0;
|
||||
if (isBuy) {
|
||||
diffMoney = _N(initAccount.Balance - nowAccount.Balance, 4);
|
||||
dealAmount = _N(nowAccount.Stocks - initAccount.Stocks, 4);
|
||||
doAmount = Math.min(maxAmount, tradeAmount - dealAmount, _N((nowAccount.Balance - 10) / tradePrice, 4));
|
||||
} else {
|
||||
diffMoney = _N(nowAccount.Balance - initAccount.Balance, 4);
|
||||
dealAmount = _N(initAccount.Stocks - nowAccount.Stocks, 4);
|
||||
doAmount = Math.min(maxAmount, tradeAmount - dealAmount, nowAccount.Stocks);
|
||||
}
|
||||
if (doAmount < e.GetMinStock()) {
|
||||
break;
|
||||
}
|
||||
prePrice = tradePrice;
|
||||
orderId = tradeFunc(tradePrice, doAmount, ticker);
|
||||
if (!orderId) {
|
||||
CancelPendingOrders(e, tradeType);
|
||||
}
|
||||
} else {
|
||||
if (mode === 0 || (Math.abs(tradePrice - prePrice) > maxSpace)) {
|
||||
orderId = null;
|
||||
}
|
||||
var order = StripOrders(e, orderId);
|
||||
if (!order) {
|
||||
orderId = null;
|
||||
}
|
||||
}
|
||||
Sleep(retryDelay);
|
||||
}
|
||||
|
||||
if (dealAmount <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
price: _N(diffMoney / dealAmount, 4),
|
||||
amount: dealAmount
|
||||
};
|
||||
}
|
||||
|
||||
var BuySpot = function(e, amount) {
|
||||
if (typeof(e) === 'number') {
|
||||
amount = e;
|
||||
e = exchange;
|
||||
}
|
||||
return Trade(e, ORDER_TYPE_BUY, amount, OpMode, SlidePrice, MaxAmount, MaxSpace, RetryDelay);
|
||||
};
|
||||
|
||||
var SellSpot = function(e, amount) {
|
||||
if (typeof(e) === 'number') {
|
||||
amount = e;
|
||||
e = exchange;
|
||||
}
|
||||
return Trade(e, ORDER_TYPE_SELL, amount, OpMode, SlidePrice, MaxAmount, MaxSpace, RetryDelay);
|
||||
};
|
||||
|
||||
// 模板内功能函数
|
||||
// - 初始化函数 tasksInit
|
||||
function TasksInit(tasks){
|
||||
_.each(tasks, function(task){ // OpenLongAmount OpenShortAmount AddAmount StopLossAmount STATE minStock initAccount Currency Name Label nowAccount lastAccount lastTASKSTATE Profit floatProfit lastPrice preProfit NowPositionInfo
|
||||
task.OpenLongAmount = 0;
|
||||
task.OpenShortAmount = 0;
|
||||
task.AddAmount = 0;
|
||||
task.StopLossAmount = 0;
|
||||
task.STATE = IDLE;
|
||||
task.minStock = _C(task.Exchange.GetMinStock);
|
||||
task.initAccount = _C(task.Exchange.GetAccount);
|
||||
task.Currency = _C(task.Exchange.GetCurrency);
|
||||
task.Name = _C(task.Exchange.GetName);
|
||||
task.Label = _C(task.Exchange.GetLabel);
|
||||
task.nowAccount = task.initAccount;
|
||||
task.lastAccount = task.initAccount;
|
||||
task.lastTASKSTATE = TASK_IDLE;
|
||||
task.Profit = 0;
|
||||
task.floatProfit = 0;
|
||||
task.lastPrice = 0;
|
||||
task.preProfit = 0;
|
||||
task.CMD = TASK_IDLE;
|
||||
task.CMD_AMOUNT = 0;
|
||||
task.NowPositionInfo = {
|
||||
avgPrice: 0, // 持仓均价
|
||||
amount: 0 , // 持仓量
|
||||
floatProfit: 0 // 浮动盈亏
|
||||
};
|
||||
Log(task.Name, task.Label, "初始账户信息:", task.initAccount);
|
||||
});
|
||||
}
|
||||
|
||||
function TranslateSTATE_TASKSTATE(STATE){
|
||||
switch(STATE){
|
||||
case 0: //TASK_IDLE
|
||||
return "无任务";
|
||||
case 1: //TASK_OPEN_LONG
|
||||
return "建多仓任务";
|
||||
case 2: //TASK_OPEN_SHORT
|
||||
return "建空仓任务";
|
||||
case 3: //TASK_ADD
|
||||
return "加仓任务";
|
||||
case 4: //TASK_ST
|
||||
return "止损任务";
|
||||
case 5: //TASK_COVER
|
||||
return "平仓任务";
|
||||
case 11: //IDLE
|
||||
return "未开仓";
|
||||
case 22: //LONG
|
||||
return "持多仓";
|
||||
case 33: //SHORT
|
||||
return "持空仓";
|
||||
}
|
||||
}
|
||||
|
||||
// - 开多仓
|
||||
function OpenLong(task){
|
||||
var BuyInfo = BuySpot(task.Exchange, task.OpenLongAmount);
|
||||
if(!BuyInfo){
|
||||
//Log("function BuySpot return :", BuyInfo);
|
||||
}else{
|
||||
task.NowPositionInfo.amount = BuyInfo.amount;
|
||||
task.NowPositionInfo.avgPrice = BuyInfo.price;
|
||||
task.STATE = LONG;
|
||||
task.nowAccount = _C(task.Exchange.GetAccount); // update nowAccount
|
||||
}
|
||||
return BuyInfo;
|
||||
}
|
||||
|
||||
// - 开空仓
|
||||
function OpenShort(task){
|
||||
var SellInfo = SellSpot(task.Exchange, task.OpenShortAmount);
|
||||
if(!SellInfo){
|
||||
//Log("function SellSpot return :", SellInfo);
|
||||
}else{
|
||||
task.NowPositionInfo.amount = SellInfo.amount;
|
||||
task.NowPositionInfo.avgPrice = SellInfo.price;
|
||||
task.STATE = SHORT;
|
||||
task.nowAccount = _C(task.Exchange.GetAccount); // update nowAccount
|
||||
}
|
||||
return SellInfo;
|
||||
}
|
||||
// - 加仓
|
||||
function AddPosition(task){
|
||||
var tradeInfo = null;
|
||||
if(task.STATE === LONG){
|
||||
tradeInfo = BuySpot(task.Exchange, task.AddAmount);
|
||||
}else if(task.STATE === SHORT){
|
||||
tradeInfo = SellSpot(task.Exchange, task.AddAmount);
|
||||
}
|
||||
if(!tradeInfo){
|
||||
//Log("function return :", tradeInfo);
|
||||
}else{
|
||||
task.NowPositionInfo.avgPrice = (tradeInfo.price * tradeInfo.amount + task.NowPositionInfo.avgPrice * task.NowPositionInfo.amount) / (tradeInfo.amount + task.NowPositionInfo.amount);
|
||||
task.NowPositionInfo.amount += tradeInfo.amount;
|
||||
task.nowAccount = _C(task.Exchange.GetAccount); // update nowAccount
|
||||
}
|
||||
return tradeInfo;
|
||||
}
|
||||
// - 止损
|
||||
function StopLoss(task){
|
||||
var tradeInfo = null;
|
||||
task.StopLossAmount = Math.min(task.StopLossAmount, task.NowPositionInfo.amount);
|
||||
if(task.STATE === LONG){
|
||||
tradeInfo = SellSpot(task.Exchange, task.StopLossAmount);
|
||||
}else if(task.STATE === SHORT){
|
||||
tradeInfo = BuySpot(task.Exchange, task.StopLossAmount);
|
||||
}
|
||||
if(!tradeInfo){
|
||||
//Log("function return :", tradeInfo);
|
||||
}else if(Math.abs(task.NowPositionInfo.amount - tradeInfo.amount) > task.minStock){
|
||||
task.NowPositionInfo.amount -= tradeInfo.amount;
|
||||
Log("未完全平仓,剩余:", Math.abs(task.NowPositionInfo.amount - tradeInfo.amount), " tradeInfo:", tradeInfo, "NowPositionInfo:", task.NowPositionInfo);
|
||||
task.nowAccount = _C(task.Exchange.GetAccount); // update nowAccount
|
||||
}else{
|
||||
task.NowPositionInfo.amount = 0;
|
||||
task.NowPositionInfo.avgPrice = 0;
|
||||
task.STATE = IDLE;
|
||||
CalProfit(task); // equal Cover
|
||||
}
|
||||
return tradeInfo;
|
||||
}
|
||||
// - 平仓
|
||||
function Cover(task){
|
||||
var tradeInfo = null;
|
||||
if(task.STATE === LONG){
|
||||
tradeInfo = SellSpot(task.Exchange, task.NowPositionInfo.amount);
|
||||
}else if(task.STATE === SHORT){
|
||||
tradeInfo = BuySpot(task.Exchange, task.NowPositionInfo.amount);
|
||||
}
|
||||
if(!tradeInfo){
|
||||
//Log("function return :", tradeInfo);
|
||||
}else if(Math.abs(task.NowPositionInfo.amount - tradeInfo.amount) > task.minStock){
|
||||
task.NowPositionInfo.amount -= tradeInfo.amount;
|
||||
Log("未完全平仓,剩余:", Math.abs(task.NowPositionInfo.amount - tradeInfo.amount), " tradeInfo:", tradeInfo, "NowPositionInfo:", task.NowPositionInfo);
|
||||
}else{
|
||||
task.NowPositionInfo.amount = 0;
|
||||
task.NowPositionInfo.avgPrice = 0;
|
||||
task.STATE = IDLE;
|
||||
CalProfit(task);
|
||||
}
|
||||
return tradeInfo;
|
||||
}
|
||||
|
||||
function CalProfit(task){
|
||||
//task.lastAccount = task.nowAccount;
|
||||
task.nowAccount = _C(task.Exchange.GetAccount);
|
||||
task.lastAccount = task.nowAccount;
|
||||
var Bdiff = task.nowAccount.Balance - task.initAccount.Balance;
|
||||
task.preProfit = task.Profit;
|
||||
task.Profit = Bdiff;
|
||||
|
||||
SumProfit = 0;
|
||||
_.each(Tasks, function(task){
|
||||
SumProfit += task.Profit;
|
||||
});
|
||||
LogProfit(SumProfit, task.Name + "-" + task.Currency + "-" + task.Label + "- 本次:" + (task.Profit - task.preProfit), "钱:" + _N(task.nowAccount.Balance, 2), "币:" + _N(task.nowAccount.Stocks, 2), "冻结钱:" + _N(task.nowAccount.FrozenBalance, 2), "冻结币:" + _N(task.nowAccount.FrozenStocks, 2));
|
||||
}
|
||||
|
||||
function CalFloatProfit(task){
|
||||
var diffPrice = 0;
|
||||
if(task.STATE === LONG){
|
||||
diffPrice = (task.lastPrice - task.NowPositionInfo.avgPrice);
|
||||
task.NowPositionInfo.floatProfit = _N(diffPrice * task.NowPositionInfo.amount);
|
||||
}else if(task.STATE === SHORT){
|
||||
diffPrice = (task.NowPositionInfo.avgPrice - task.lastPrice);
|
||||
task.NowPositionInfo.floatProfit = _N(diffPrice * task.NowPositionInfo.amount);
|
||||
}
|
||||
}
|
||||
|
||||
function isInCMD_List(cmd){
|
||||
for(var i = 0; i < cmdList.length ; i++){
|
||||
if(cmd === cmdList[i]){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function CMD(index, CMD_STR, amount){
|
||||
if(index < Tasks.length || typeof(amount) === 'undefined' || isInCMD_List(CMD_STR) === false){
|
||||
Tasks[index].CMD = CMD_STR;
|
||||
Tasks[index].CMD_AMOUNT = amount;
|
||||
}else{
|
||||
Log("错误:", "index:" + index, "CMD_STR:" + CMD_STR, "amount:" + amount);
|
||||
}
|
||||
}
|
||||
|
||||
$.TaskCmd = function(cmd, amount, lastPrice){
|
||||
if(cmd === TASK_IDLE && typeof(amount) === 'undefined'){
|
||||
amount = 0;
|
||||
}else if((cmd === TASK_ST || cmd === TASK_COVER) && typeof(amount) === 'undefined'){
|
||||
amount = 0;
|
||||
}else if(typeof(amount) === 'undefined'){
|
||||
throw "没有传入要操作下单量!";
|
||||
}
|
||||
if(typeof(lastPrice) === 'undefined'){
|
||||
return {cmd: cmd, amount: amount, lastPrice: -1};
|
||||
}else{
|
||||
return {cmd: cmd, amount: amount, lastPrice: lastPrice};
|
||||
}
|
||||
}
|
||||
|
||||
// 模板导出函数
|
||||
var usedTime = 0;
|
||||
$.Trend = function() {
|
||||
TasksInit(Tasks);
|
||||
while(true){
|
||||
var beginTime = new Date().getTime();
|
||||
_.each(Tasks, function(task){
|
||||
task.OpenLongAmount = 0;
|
||||
task.OpenShortAmount = 0;
|
||||
task.AddAmount = 0;
|
||||
task.StopLossAmount = 0;
|
||||
});
|
||||
_.each(Tasks, function(task){
|
||||
var obj = task.onTick();
|
||||
//Log("obj:", obj);
|
||||
var ret = obj.cmd;
|
||||
var amount = obj.amount;
|
||||
var tradeRet = null;
|
||||
if(obj.lastPrice !== -1){
|
||||
task.lastPrice = obj.lastPrice;
|
||||
}else{
|
||||
task.lastPrice = (_C(task.Exchange.GetTicker)).Last;
|
||||
}
|
||||
if(task.CMD !== TASK_IDLE){
|
||||
ret = task.CMD;
|
||||
amount = task.CMD_AMOUNT;
|
||||
}
|
||||
switch (ret) {
|
||||
// switch status..
|
||||
case TASK_OPEN_LONG: // 开多仓
|
||||
if(task.STATE === IDLE){
|
||||
task.lastTASKSTATE = TASK_OPEN_LONG;
|
||||
task.OpenLongAmount = amount;
|
||||
tradeRet = OpenLong(task);
|
||||
Log("开多仓完成,本次交易信息:", tradeRet, "#FF0000");
|
||||
}else{
|
||||
//Log("TASK_OPEN_LONG 当前状态为:", task.STATE, "!= ", IDLE);
|
||||
}
|
||||
break;
|
||||
case TASK_OPEN_SHORT: // 开空仓
|
||||
if(task.STATE === IDLE){
|
||||
task.lastTASKSTATE = TASK_OPEN_SHORT;
|
||||
task.OpenShortAmount = amount;
|
||||
tradeRet = OpenShort(task);
|
||||
Log("开空仓完成,本次交易信息:", tradeRet, "#FF0000");
|
||||
}else{
|
||||
//Log("TASK_OPEN_SHORT 当前状态为:", task.STATE, "!= ", IDLE);
|
||||
}
|
||||
break;
|
||||
case TASK_ADD: // 加仓
|
||||
if(task.STATE === LONG || task.STATE === SHORT){
|
||||
task.lastTASKSTATE = TASK_ADD;
|
||||
task.AddAmount = amount;
|
||||
tradeRet = AddPosition(task);
|
||||
Log("加仓完成,本次交易信息:", tradeRet, "#FF0000");
|
||||
}else{
|
||||
//Log("TASK_ADD 当前状态为:", task.STATE, "!=", LONG, "或者", SHORT);
|
||||
}
|
||||
break;
|
||||
case TASK_ST: // 止损
|
||||
if(task.STATE === LONG || task.STATE === SHORT){
|
||||
task.lastTASKSTATE = TASK_ST;
|
||||
task.StopLossAmount = amount;
|
||||
tradeRet = StopLoss(task);
|
||||
Log("止损完成,本次交易信息:", tradeRet, "#FF0000");
|
||||
}else{
|
||||
//Log("TASK_ST 当前状态为:", task.STATE, "!=", LONG, "或者", SHORT);
|
||||
}
|
||||
break;
|
||||
case TASK_COVER: // 平仓
|
||||
if(task.STATE === LONG || task.STATE === SHORT){
|
||||
task.lastTASKSTATE = TASK_COVER;
|
||||
tradeRet = Cover(task);
|
||||
Log("平仓完成,本次交易信息:", tradeRet, "#FF0000");
|
||||
}else{
|
||||
//Log("TASK_COVER 当前状态为:", task.STATE, "!= ", LONG, "或者", SHORT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// 计算浮动收益
|
||||
CalFloatProfit(task);
|
||||
|
||||
// 重置CMD
|
||||
if(task.CMD !== TASK_IDLE){
|
||||
task.CMD = TASK_IDLE;
|
||||
task.CMD_AMOUNT = 0;
|
||||
}
|
||||
});
|
||||
|
||||
// 交互模块
|
||||
var cmd = GetCommand(); // 调用API 获取界面交互控件的消息。
|
||||
if (cmd) { // 判断是否有消息
|
||||
var js = cmd.split(':', 2)[1]; // 分割 返回的消息 字符串, 限制返回2个, 把索引为1的 元素 赋值给 名为js 的变量
|
||||
Log("执行代码:", js); // 输出 执行的代码
|
||||
try { // 异常检测
|
||||
eval(js); // 执行 eval函数, 该函数执行传入的参数(代码)。
|
||||
} catch (e) { // 抛出异常
|
||||
Log("交互代码错误: Exception", e); // 输出错误信息
|
||||
}
|
||||
}
|
||||
|
||||
// 显示在状态栏
|
||||
var endTime = new Date().getTime();
|
||||
usedTime = endTime - beginTime;
|
||||
$.ToTable();
|
||||
Sleep(Interval);
|
||||
}
|
||||
}
|
||||
|
||||
$.Relation_Exchange_onTick = function(Exchange, onTick){
|
||||
var task = {
|
||||
onTick : onTick,
|
||||
Exchange : Exchange
|
||||
};
|
||||
Tasks.push(task);
|
||||
};
|
||||
|
||||
$.ToTable = function(){
|
||||
var tables = [];
|
||||
_.each(Tasks, function(task){
|
||||
var table = {type: "table", title: task.Exchange.GetName() + task.Exchange.GetLabel(), cols: ["desc", "value"], rows: []};
|
||||
table.rows.push(["初始账户信息:", task.initAccount]);
|
||||
table.rows.push(["当前账户信息:", task.nowAccount]);
|
||||
table.rows.push(["上次平仓后账户信息:", task.lastAccount]);
|
||||
table.rows.push(["持仓信息:", task.NowPositionInfo]);
|
||||
table.rows.push(["状态:", TranslateSTATE_TASKSTATE(task.STATE)]);
|
||||
table.rows.push(["最近任务:", TranslateSTATE_TASKSTATE(task.lastTASKSTATE)]);
|
||||
table.rows.push(["浮动盈亏:", task.floatProfit = task.NowPositionInfo.floatProfit]);
|
||||
table.rows.push(["总盈亏:", task.Profit]);
|
||||
// 处理自定义
|
||||
for(var key in task){
|
||||
if(key === 'OpenLongAmount' || key === 'OpenShortAmount' || key === 'AddAmount' || key === 'StopLossAmount' || key === 'STATE' || key === 'minStock' || key === 'initAccount' || key === 'Currency' ||
|
||||
key === 'Name' || key === 'Label' || key === 'nowAccount' || key === 'lastAccount' || key === 'lastTASKSTATE' || key === 'Profit' || key === 'floatProfit' || key === 'lastPrice' || key === 'preProfit' || key === 'NowPositionInfo' ||
|
||||
key === 'onTick' || key === 'Exchange' || key === 'CMD' || key === 'CMD_AMOUNT'){
|
||||
continue;
|
||||
}else{
|
||||
table.rows.push([key, task[key]]);
|
||||
}
|
||||
}
|
||||
|
||||
tables.push(table);
|
||||
});
|
||||
var CMD_STR = "TASK_OPEN_LONG : 开多仓, TASK_OPEN_SHORT : 开空仓, TASK_ADD : 加仓, TASK_ST : 止损, TASK_COVER : 平仓 , 命令函数:CMD(index, CMD_STR, amount)";
|
||||
LogStatus(" time: " + _D() + " 耗时:" + usedTime + "合计收益:" + SumProfit + "命令:" + '\n' + CMD_STR + '\n`' + JSON.stringify(tables) + '`');
|
||||
};
|
||||
|
||||
$.AddData = function(index, dataKey, dataValue){
|
||||
Tasks[index][dataKey] = dataValue;
|
||||
};
|
||||
|
||||
// 以下是测试代码
|
||||
/*- 状态 在使用 模板时需要在 主策略内声明
|
||||
var TASK_IDLE = 0;
|
||||
var TASK_OPEN_LONG = 1;
|
||||
var TASK_OPEN_SHORT = 2;
|
||||
var TASK_ADD = 3;
|
||||
var TASK_ST = 4;
|
||||
var TASK_COVER = 5;
|
||||
*/
|
||||
|
||||
function onTick1() {
|
||||
// EMA
|
||||
var records = _C(exchanges[0].GetRecords);
|
||||
if(records.length < 11){
|
||||
return $.TaskCmd(TASK_IDLE);
|
||||
}
|
||||
var ema_fast = TA.MA(records, 7);
|
||||
var ema_slow = TA.MA(records, 20);
|
||||
// $.AddData = function(index, dataKey, dataValue)
|
||||
|
||||
var data = "fast[-2]:" + ema_fast[ema_fast.length - 2] + " slow[-2]" + ema_slow[ema_slow.length - 2] + " fast[-1]:" + ema_fast[ema_fast.length - 1] + " slow[-1]:" + ema_slow[ema_slow.length - 1];
|
||||
$.AddData(0, "MA", data);
|
||||
|
||||
if (ema_fast[ema_fast.length - 1] < ema_slow[ema_slow.length - 1] && ema_fast[ema_fast.length - 2] > ema_slow[ema_slow.length - 2]) {
|
||||
return $.TaskCmd(TASK_COVER);
|
||||
}else if(ema_fast[ema_fast.length - 1] > ema_slow[ema_slow.length - 1] && ema_fast[ema_fast.length - 2] < ema_slow[ema_slow.length - 2]){
|
||||
return $.TaskCmd(TASK_OPEN_LONG, 0.5);
|
||||
}
|
||||
|
||||
return $.TaskCmd(TASK_IDLE);
|
||||
}
|
||||
|
||||
function onTick2() {
|
||||
// MACD
|
||||
var records = _C(exchanges[1].GetRecords);
|
||||
if(records.length < 15){
|
||||
$.TaskCmd(TASK_IDLE);
|
||||
}
|
||||
var macd = TA.MACD(records);
|
||||
var dif = macd[0];
|
||||
var dea = macd[1];
|
||||
|
||||
var data = "dif[-2]:" + dif[dif.length - 2] + " dea[-2]" + dea[dea.length - 2] + " dif[-1]:" + dif[dif.length - 1] + " dea[-1]:" + dea[dea.length - 1];
|
||||
$.AddData(1, "MACD", data);
|
||||
|
||||
if (dif[dif.length - 1] > dea[dea.length - 1] && dif[dif.length - 2] < dea[dea.length - 2]) {
|
||||
//return $.TaskCmd(TASK_OPEN_SHORT, 0.8);
|
||||
return $.TaskCmd(TASK_COVER);
|
||||
}else if(dif[dif.length - 1] < dea[dea.length - 1] && dif[dif.length - 2] > dea[dea.length - 2]){
|
||||
//return $.TaskCmd(TASK_COVER);
|
||||
return $.TaskCmd(TASK_OPEN_LONG, 0.8);
|
||||
}
|
||||
|
||||
return $.TaskCmd(TASK_IDLE);
|
||||
}
|
||||
|
||||
function main() {
|
||||
$.Relation_Exchange_onTick(exchanges[0], onTick1);
|
||||
$.Relation_Exchange_onTick(exchanges[1], onTick2);
|
||||
$.Trend(); // 不用传参数。
|
||||
}
|
||||
Reference in New Issue
Block a user