This commit is contained in:
botvs
2017-01-26 22:04:56 +08:00
parent a980727a02
commit cd7aec2508
10 changed files with 1693 additions and 652 deletions
+5 -5
View File
@@ -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)
+233
View File
@@ -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()
}
}
-106
View File
@@ -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
*/
-28
View File
@@ -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);
}
}
+1 -1
View File
@@ -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
View File
@@ -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(); // 不用传参数。
}
+615
View File
@@ -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(); // 不用传参数。
}