资源加载中... loading...

现现对冲(多交易所搬砖)

Author: vipjohn, Date: 2019-07-15 22:15:22
Tags: 对冲套利

策略功能: 实现跨交易所的现现对冲 支持无限个交易所同时搬砖 支持任意交易对搬砖 详细报表支持(完整的状态信息, 可实时看到策略观察到的各个交易所的差价信息, 持仓信息,资金净值,策略运行状况) 24小时无人值守, 全自动运行

策略参数设置: 请访问 https://www.pcclean.io/9wbl


/*
现现对冲(搬砖)

注意:
1.确保设置了交易所的手续费和最小买入量

版本历史:
21:33 2018/10/25 处理挂单不成交的情况,不再使用市价单
7:28 2018/10/26 支持永久记录收益
8:00 2018/10/27 支持显示币数变化,搬砖函数每次更新getaccount
10:14 2018/10/27 修复不显示profit chart的问题
*/

//搬砖参数设置
var handfee=  {FCoin:0.0 , CoinEx:0.0004,Huobi:0.002,OKEX:0.002,Binance:0.001,ZB:0.002 };//手续费
var minestbuy={FCoin:0.01, CoinEx:0.01,  Huobi:0.01, OKEX:0.01, Binance:0.04, ZB:0.01  }; //最小买入量
var want_profit=0.0001;//期望收益
var price_step=0.00001;//定价单调整价格的单位
var maxbuy=5; //最大买入
var warning_stocks=10;//余额预警
var price_n=6; //价格精度
var num_n=3; //数量精度
var max_wait_order=15000;//订单等待时间
var buypercent=1.0; //买入百分比
var wait_ms=3000;//重试等待间隔时间
var enable_traders_recorder=true;//记录所有交易


//全局变量
var limit_orders=[];//限价单
var total_banzhuan=0;//搬砖次数
var total_op=0;//总搬砖机会
var exchange_banzhuan={};//记录交易所搬砖次数
var total_passed=0;//因为余额不足错过了多少次搬砖机会
var trades=[];//所有交易
var approximate_profit=0;//盈亏近似值
var dealamount_change=0;//币数变化

function get_exchange_banzhuan(exname){
	var name = exname.replace(" ", "_");
	if (isNaN(exchange_banzhuan[name])){
		return 0;
	}else{
		return exchange_banzhuan[name];
	}
}
function put_exchange_banzhuan(exname,value){
	var name = exname.replace(" ", "_");
	exchange_banzhuan[name]=value;
}

//在交易所ex1和ex2之间搬砖
function banzhuan(exchange1,exchange2) 
{
	//read price from exchange 1
	var account1=_C(exchange1.GetAccount);
	var exname1=exchange1.GetName();
	var depth1 = _C(exchange1.GetDepth);
	var askprice1=depth1.Asks[0].Price;
	var askamount1=depth1.Asks[0].Amount;
	
	//read price from exchange 2
	var account2=_C(exchange2.GetAccount);
	var exname2=exchange2.GetName();
	var depth2 = _C(exchange2.GetDepth);
	var bidprice2=depth2.Bids[0].Price;
	var bidamount2=depth2.Bids[0].Amount;
	
	var threshold=handfee[exname1]+handfee[exname2]+want_profit; //交易阀值
	var minbuy=Math.max(minestbuy[exname1],minestbuy[exname2]); //最小购买数量	
	
	//buy and sell if its prifitable
	var diff=(bidprice2-askprice1)/askprice1;
	var canbuy=Math.min(askamount1,bidamount2);
	canbuy=canbuy*buypercent;
	var min=Math.min(canbuy,maxbuy); //the minimum number for buy/sell 
	
	if (Math.abs(diff)>=threshold && min>=minbuy) //for bch
	{
		if (diff>0)//exchange 1 is cheap
		{
			total_op=total_op+2;
			put_exchange_banzhuan(exname1+"_op",get_exchange_banzhuan(exname1+"_op")+1);
			put_exchange_banzhuan(exname2+"_op",get_exchange_banzhuan(exname2+"_op")+1);
			put_exchange_banzhuan(exname1+"_canbuy",get_exchange_banzhuan(exname1+"_canbuy")+min);
			put_exchange_banzhuan(exname2+"_cansell",get_exchange_banzhuan(exname2+"_cansell")+min);
				
			var needmoney=min*askprice1;
			if (account2.Stocks>min && account1.Balance>needmoney ){
				
				//buy from exchange 1
				var price_1=askprice1;
				var buyID=retryBuy(exchange1,price_1,min);
				//sell from exchange 2
				var price_2=bidprice2;
				var sellID=retrySell(exchange2,price_2,min);
				
				//add orders to pre-processing list
				var order1={
					exchange:exchange1,
					ID:buyID,
					create_time:new Date(),
					utcdate:get_ChinaTimeString()
				};
				limit_orders.push(order1);
				var order2={
					exchange:exchange2,
					ID:sellID,
					create_time:new Date(),
					utcdate:get_ChinaTimeString()
				};
				limit_orders.push(order2);
				
				total_banzhuan=total_banzhuan+2;
				put_exchange_banzhuan(exname1,get_exchange_banzhuan(exname1)+1);
				put_exchange_banzhuan(exname2,get_exchange_banzhuan(exname2)+1);
			}else{
				if (account2.Stocks<=min){
					Log(exchange2.GetName()+"没币了,无法搬砖。请及时充值。"+"#ff0000");
				}
				if (account1.Balance<=needmoney){
					Log(exchange1.GetName()+"没钱了,无法搬砖。请及时充值。"+"#ff0000");
				}
				total_passed=total_passed+2;
			}
		}
	}
}


//检查订单是否执行成功,如果未完成,执行市价下单
function process_limiteorders(){
	var cur_time=new Date();
	var limit_orders_new=[];
	for (var i=0; i<limit_orders.length; ++i){
		var create_time=limit_orders[i].create_time;
		var passedtime=cur_time-create_time;
		if (passedtime>max_wait_order){
			var exchange_c=limit_orders[i].exchange;
			var order_ID=limit_orders[i].ID;
			var exname=exchange_c.GetName();
			var account=_C(exchange_c.GetAccount);
			var ticker=_C(exchange_c.GetTicker);
			var last=ticker.Last;
			var orderdata=_C(exchange_c.GetOrder,order_ID);
			var type=orderdata.Type;
			
			if (orderdata.Status!=ORDER_STATE_CLOSED){
				var notcompleted=orderdata.Amount-orderdata.DealAmount;
				exchange_c.CancelOrder(order_ID);
				if (type===ORDER_TYPE_BUY){
					var allowbuy=Math.min(account.Balance/last,notcompleted);
					if (allowbuy>minestbuy[exname]){
						var limited_order_ID=retryBuy(exchange_c,orderdata.Price+price_step,allowbuy);
						Log("加价买入。"+orderdata.Price+"|"+(orderdata.Price+price_step));
						var limited_order_data={
							exchange:exchange_c,
							ID:limited_order_ID,
							create_time:new Date(),
							utcdate:get_ChinaTimeString()
						};
						limit_orders_new.push(limited_order_data);
					}
					approximate_profit-=(orderdata.Price*orderdata.DealAmount*(1+handfee[exname]));
					dealamount_change+=(orderdata.DealAmount);
				}else if (type===ORDER_TYPE_SELL){
					var allowsell=Math.min(account.Stocks,notcompleted);
					if (allowsell>minestbuy[exname]){
						var limited_order_ID=retrySell(exchange_c,orderdata.Price-price_step,allowsell);
						Log("降价卖出。"+orderdata.Price+"|"+(orderdata.Price-price_step));
						var limited_order_data={
							exchange:exchange_c,
							ID:limited_order_ID,
							create_time:new Date(),
							utcdate:get_ChinaTimeString()
						};
						limit_orders_new.push(limited_order_data);
					}
					approximate_profit+=(orderdata.Price*orderdata.DealAmount*(1-handfee[exname]));
					dealamount_change-=(orderdata.DealAmount);
				}
				
				//保存交易信息
				if (type===ORDER_TYPE_BUY){
					var details={
						type:"限价买",
						time:limit_orders[i].utcdate,
						RealAmount:orderdata.DealAmount,
						WantAmount:orderdata.Amount,
						RealPrice:orderdata.Price,
						WantPrice:orderdata.Price,
						Memo:"部分完成",
						exname:exname
						};
					if (enable_traders_recorder){
						trades.push(details);
					}
				}else if (type===ORDER_TYPE_SELL){
					var details={
						type:"限价卖",
						time:limit_orders[i].utcdate,
						RealAmount:orderdata.DealAmount,
						WantAmount:orderdata.Amount,
						RealPrice:orderdata.Price,
						WantPrice:orderdata.Price,
						Memo:"部分完成",
						exname:exname
						};
					if (enable_traders_recorder){
						trades.push(details);
					}
				}
			}else{
				//保存交易信息
				if (type===ORDER_TYPE_BUY){
					approximate_profit-=(orderdata.Price*orderdata.DealAmount*(1+handfee[exname]));
					dealamount_change+=(orderdata.DealAmount);
					var details={
						type:"限价买",
						time:limit_orders[i].utcdate,
						RealAmount:orderdata.DealAmount,
						WantAmount:orderdata.Amount,
						RealPrice:orderdata.Price,
						WantPrice:orderdata.Price,
						Memo:"已完成",
						exname:exname
						};
					if (enable_traders_recorder){
						trades.push(details);
					}
				}else if (type===ORDER_TYPE_SELL){
					approximate_profit+=(orderdata.Price*orderdata.DealAmount*(1-handfee[exname]));
					dealamount_change-=(orderdata.DealAmount);
					var details={
						type:"限价卖",
						time:limit_orders[i].utcdate,
						RealAmount:orderdata.DealAmount,
						WantAmount:orderdata.Amount,
						RealPrice:orderdata.Price,
						WantPrice:orderdata.Price,
						Memo:"已完成",
						exname:exname
						};
					if (enable_traders_recorder){
						trades.push(details);
					}
				}
			}
		}else{
			limit_orders_new.push(limit_orders[i]);
		}
	}
	limit_orders=limit_orders_new;
}

//重试购买,直到成功返回
function retryBuy(ex,price,num)
{
	var r=ex.Buy(_N(price,price_n), _N(num,num_n));
	while (!r){
		Log("Buy失败,正在retry。");
		Sleep(wait_ms);
		r=ex.Buy(_N(price,price_n), _N(num,num_n));
	}
	return r;
}

//重试卖出,直到成功返回
function retrySell(ex,price,num)
{
	var r=ex.Sell(_N(price,price_n), _N(num,num_n));
	while (!r){
		Log("Sell失败,正在retry。");
		Sleep(wait_ms);
		r=ex.Sell(_N(price,price_n), _N(num,num_n));
	}
	return r;
}

//get utc+8 date string
function get_ChinaTimeString()
{
	var date = new Date(); 
	var now_utc =  Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
	var cdate=new Date(now_utc);
	cdate.setHours(cdate.getHours()+8);
	var localstring=cdate.getFullYear()+'/'+(cdate.getMonth()+1)+'/'+cdate.getDate()+' '+cdate.getHours()+':'+cdate.getMinutes()+':'+cdate.getSeconds();
	return localstring;
}


//主逻辑
function main(){

	_CDelay(wait_ms);
	var exchange_num=exchanges.length;
	var pre_time = new Date();
	var start_time=new Date();
	var last_profit=0;
	var pre_profit=Number(_G("pre_profit"));
	Log('之前收入累计:'+pre_profit);

    while(true){	
		var cur_time = new Date();
		var passedtime=cur_time-pre_time;
		pre_time=cur_time;
		
		for (var i=0; i<exchange_num-1; ++i)
		{
			for (var j=i+1;j<exchange_num; ++j)
			{
				banzhuan(exchanges[i],exchanges[j]);
				banzhuan(exchanges[j],exchanges[i]);
			}
		}		
		
		process_limiteorders();

		var total_orders_notcomplete=0;
		var total_balance=0;
		var table1 = {type: 'table', title: '账户信息', cols: ['交易所', '最新价','可用币','可用余额','资产','手续费率','最小购买数量','未完成订单','实搬/搬砖机会','可买/可卖'], rows: []}; 
		var table2 = {type: 'table', title: '状态', cols: ['轮询时间','实际搬砖次数','错过搬砖次数','每天搬砖机会','当前总资产','【近似收益】','币数变化','未完成订单总数'], rows: []}; 
		var table3 = {type: 'table', title: '交易历史', cols: ['交易所','日期','类型', '成交数量','发单数量','成交价','发单价','备注'], rows: []};
		for (i=0; i<exchange_num; ++i){
			var exname=exchanges[i].GetName();
			var account=_C(exchanges[i].GetAccount);
			var ticker=_C(exchanges[i].GetTicker);
			var last=ticker.Last;
			var orders_not_complete=_C(exchanges[i].GetOrders);
			total_orders_notcomplete+=orders_not_complete.length;
			var balance=(account.Balance+account.FrozenBalance)+(account.Stocks+account.FrozenStocks)*last;
			total_balance+=balance;
			table1.rows.push([exname,last,
				account.Stocks+(account.Stocks<=warning_stocks?"#ff0000":"#00ff00"),
				account.Balance+(account.Balance<=warning_stocks*last?"#ff0000":"#00ff00"),
				balance,handfee[exname],minestbuy[exname],orders_not_complete.length,
				get_exchange_banzhuan(exname)+'/'+get_exchange_banzhuan(exname+"_op"),
				get_exchange_banzhuan(exname+"_canbuy")+'/'+get_exchange_banzhuan(exname+"_cansell")
				]);
		}
		table2.rows.push([passedtime+'ms',total_banzhuan,total_passed,
			total_op/((new Date()-start_time)/1000/60/60/24),
			total_balance+' '+exchanges[0].GetQuoteCurrency(),
			approximate_profit+' '+exchanges[0].GetQuoteCurrency(),
			dealamount_change, total_orders_notcomplete
			]);
		for (i=0; i < trades.length; i++){
			table3.rows.push([trades[i].exname,trades[i].time,trades[i].type,trades[i].RealAmount,trades[i].WantAmount,trades[i].RealPrice,trades[i].WantPrice,trades[i].Memo]);
		}
		
		//show tables
		LogStatus('`' + JSON.stringify([table1,table2,table3])+'`'+'\n'+
			' *提示:红色代表余额可能不足。'+'\n'+
			'定制策略请联系微信: alinwo (验证消息: botvs)#0000ff'+'\n');
		
		//show profit chart
		if (last_profit!==approximate_profit && total_orders_notcomplete===0 && limit_orders.length===0){
			LogProfit(pre_profit+approximate_profit);
			_G("pre_profit", pre_profit+approximate_profit);
			last_profit=approximate_profit;
		}
	}
}


template: strategy.tpl:40:21: executing "strategy.tpl" at <.api.GetStrategyListByName>: wrong number of args for GetStrategyListByName: want 7 got 6