private TradeOrder SplitOrder(TradeOrder source, int splitContracts) { TradeOrder cTradeOrder = source.Clone(); //將原始下單資訊 Clone 一份 cTradeOrder.Contracts = splitContracts; //修改成要分拆的數量 int iContracts = source.Contracts; if (iContracts > 1) //如果原始的量超過 1 就計算手續費跟交易稅的平均值(有可能成交數量 > 1, 手續費與交易稅會合併計算) { cTradeOrder.Fee = source.Fee / iContracts * splitContracts; cTradeOrder.Tax = source.Tax / iContracts * splitContracts; source.Fee -= cTradeOrder.Fee; source.Tax -= cTradeOrder.Tax; } source.Contracts -= splitContracts; //分拆後的數量要從原始的下單資訊內扣除 return(cTradeOrder); //回傳分拆後的下單資訊 }
/// <summary> /// 計算倉位 /// </summary> /// <param name="queue">儲存留倉資訊的 Queue</param> /// <param name="trade">交易資訊</param> private void CalculatePosition(Queue <Trade> queue, Trade trade) { lock (queue) { TradeOrder cTargetOrder = trade._EntryOrder; while (queue.Count > 0 && cTargetOrder.Contracts > 0) { Trade cTrade = queue.Peek(); TradeOrder cTradeOrder = cTrade._EntryOrder; if (cTradeOrder.Contracts > cTargetOrder.Contracts) //如果留倉單數量大於平倉單成交數量 { trade._ExitOrder = cTargetOrder; //將平倉單直接放入 _ExitOrder trade._EntryOrder = SplitOrder(cTradeOrder, cTargetOrder.Contracts); //從留倉單 Clone 後拆出與平倉單相同的數量, 放入 _EntryOrder trade.CalculateProfit(__dBigPointValue); //計算利潤 lock (__cHistorys) { __cHistorys.Add(trade); //寫入歷史資訊列表 } SetParameters(trade); //計算內部其他參數 break; //直接離開(因為已經從倉部位扣掉平倉數量, 所以直接離開) } else //如果留倉單數量小於或等於平倉單數量 { cTrade.IsTradeClosed = true; cTrade._ExitOrder = SplitOrder(cTargetOrder, cTradeOrder.Contracts); //將平倉單 Clone 後拆出與留倉單相同數量, 放入 _ExitOrder cTrade.CalculateProfit(__dBigPointValue); //計算利潤 lock (__cHistorys) { __cHistorys.Add(cTrade); //寫入歷史資訊列表 } SetParameters(cTrade); //計算內部其他參數 queue.Dequeue(); //將此筆留倉單從 queue 內移除(表示此筆留倉單已經被沖銷) this.Remove(cTradeOrder.Ticket); //被沖銷的留倉單就從倉部位內移除掉 } } } }
private void LargeOrderService_onResponse(object sender, ResponseEvent e) { switch (e.ResponseType) { case ResponseType.Cancel: lock (__oLock) { if (__cCurrent != null) { EOrderAction cAction = __cCurrent.Action; __cTemp = (__bReverse && (cAction == EOrderAction.Sell || cAction == EOrderAction.BuyToCover)) ? null : __cCurrent; } __cCurrent = null; } break; case ResponseType.Deal: lock (__oLock) { if (__cCurrent != null && __cCurrent.Contracts == 0) { __cTemp = null; __cCurrent = null; } } break; case ResponseType.Trust: lock (__oLock) { if (__cCurrent == null) { __cTemp = null; __cCurrent = e.TradeOrder as TradeOrder; } } break; } }
private void PipeStream_onMessage(object sender, MessageEvent e) { if (e.Buffer[0] == 0x7b) { //檢查是否為 JSON 開頭符號 JToken cToken = JsonConvert.DeserializeObject<JToken>(e.Message); int iDataType = cToken["Type"].Value<int>(); switch (iDataType) { case 0: //接收 REPOTR 完畢的回報類型(如果沒收到此命令會無窮盡等待) __cResetEvent.Set(); if (logger.IsInfoEnabled) logger.Info("[Report] Order data response completed..."); break; case 1: //委託回報 string sOrderSymbolId = null; OrderTrust[] cTrusts = cToken["Report"].ToObject<OrderTrust[]>(); foreach (OrderTrust cTrust in cTrusts) { string sTrustId = cTrust.委託書號; int iTrustCount = cTrust.未成交數量; TradeOrder cTrustOrder = __cEntrusts.GetTrade(sTrustId); //取得委託單 if (cTrustOrder == null && iTrustCount > 0) { //委託未成交數量大於0才處理 string sSymbolId = cTrust.商品代號; cTrustOrder = GetWaiting(sSymbolId, cTrust.委託價格); //取得等待委託回報的委託單(這些委託單都要等待委託單號) if (cTrustOrder == null) { if (__iMaxTrustIndex == __iTrustIndex) { //相等表示沒有下出任何的委託單, 可能是留在下單機內的委託單 if (sOrderSymbolId == null) { sOrderSymbolId = GetOrderSymbol(); } if (CheckSymbol(sSymbolId, sOrderSymbolId)) { //比對兩個商品代號是否相同, 相同才會被加入委託倉內 cTrustOrder = new TradeOrder(); cTrustOrder.IsSended = true; cTrustOrder.IsTrusted = true; cTrustOrder.Name = sTrustId; cTrustOrder.Ticket = sTrustId; cTrustOrder.SymbolId = sSymbolId; cTrustOrder.Price = cTrustOrder.Price; cTrustOrder.Contracts = iTrustCount; cTrustOrder.Time = cTrust.委託時間; cTrustOrder.Category = (cTrustOrder.Price == 0) ? OrderCategory.Market : OrderCategory.Limit; if (cTrust.倉別.Equals(__sClosedString)) { cTrustOrder.Action = (cTrust.買賣別) ? EOrderAction.BuyToCover : EOrderAction.Sell; } else { cTrustOrder.Action = (cTrust.買賣別) ? EOrderAction.Buy : EOrderAction.SellShort; } __cEntrusts.Add(cTrustOrder); //儲存至委託陣列內 } } else { continue; } } else { cTrustOrder.IsTrusted = true; //true=委託成功 cTrustOrder.Ticket = sTrustId; //填入回報來的委託單號 cTrustOrder.Time = cTrust.委託時間; __cEntrusts.Add(cTrustOrder); //儲存至委託陣列內 } } if (cTrustOrder != null) { bool bDelete = cTrust.是否刪單; if (bDelete) { __cEntrusts.Remove(sTrustId); //從委託陣列內移除委託 } OnResponse(cTrustOrder, cTrustOrder.SymbolId, (bDelete) ? ResponseType.Cancel : ResponseType.Trust); if (logger.IsInfoEnabled) logger.InfoFormat("[Trust] #{0} {1} {2} {3} at {4} {5} @{6}", cTrustOrder.Ticket, cTrustOrder.SymbolId, cTrustOrder.Action, (bDelete) ? cTrustOrder.Contracts : iTrustCount, cTrustOrder.Price, (bDelete) ? cTrust.備註 : (iTrustCount == 0) ? __sFullDeal : cTrustOrder.Name, cTrustOrder.Time.ToString("yyyy-MM-dd HH:mm:ss")); } } break; case 2: //成交回報 OrderDeal cDeal = cToken["Report"].ToObject<OrderDeal>(); string sDealId = cDeal.成交書號; //成交書號 == 委託書號 TradeOrder cTempTrust = __cEntrusts.GetTrade(sDealId); if (cTempTrust != null) { //檢查委託陣列內是否有相同的成交書號(委託書號) int iDealLots = cDeal.成交數量; cTempTrust.Contracts -= iDealLots; bool bDealed = cTempTrust.Contracts == 0; if (bDealed) { OnResponse(cTempTrust, cTempTrust.SymbolId, ResponseType.Trust); if (logger.IsInfoEnabled) logger.InfoFormat("[Trust] #{0} {1} {2} {3} at {4} {5} @{6}", cTempTrust.Ticket, cTempTrust.SymbolId, cTempTrust.Action, 0, cTempTrust.Price, __sFullDeal, cTempTrust.Time.ToString("yyyy-MM-dd HH:mm:ss")); } TradeOrder cDealOrder = cTempTrust.Clone(); cDealOrder.IsDealed = cTempTrust.Contracts == 0; cDealOrder.Contracts = iDealLots; cDealOrder.Price = cDeal.成交價格; cDealOrder.Fee = cDeal.手續費; cDealOrder.Tax = cDeal.交易稅; cDealOrder.Time = cDeal.成交時間; double[] dValues = CalculateCommissions(cDealOrder); //計算其他佣金與手續費(通常真實交易會回報交易稅與眷商手續費, 這裡還可以計算其他的附加費用或是附加的手續費) cDealOrder.OtherFees = dValues[0]; cDealOrder.Fee += dValues[1]; if (logger.IsInfoEnabled) logger.InfoFormat("[Deal] #{0} {1} {2} {3} at {4} {5} @{6}", cDealOrder.Ticket, cDealOrder.SymbolId, cDealOrder.Action, cDealOrder.Contracts, cDealOrder.Price, cDealOrder.Name, cDealOrder.Time.ToString("yyyy-MM-dd HH:mm:ss")); lock (__cDeals) { __cDeals.Enqueue(cDealOrder); } cTempTrust.IsDealed = true; } break; } } }
private bool CheckTrust(EOrderAction action, OrderCategory category, double limitPrice, int lots, string name, bool isReverse, bool openNextBar) { TradeOrder cTrust = __cEntrusts.GetTradeFromName(name); if (cTrust == null) { if (action == EOrderAction.Sell || action == EOrderAction.BuyToCover) { bool bRet = false; int iCount = __cEntrusts.Count; if (iCount > 0) { EOrderAction cAction = (action == EOrderAction.Buy || action == EOrderAction.BuyToCover) ? EOrderAction.SellShort : (action == EOrderAction.SellShort || action == EOrderAction.Sell) ? EOrderAction.Buy : action; for (int i = 0; i < iCount; i++) { TradeOrder cTemp = __cEntrusts[i]; if (cTemp.IsTrusted && cTemp.Action == cAction && cTemp.Contracts > 0) { if (!cTemp.IsCancel) { SendTrust(cTemp, cTemp.Ticket); //取消委託單 cTemp.IsCancel = true; } bRet = true; } } } if (bRet || __cDeals.Count > 0) { return false; } } } else { if (openNextBar) { if (!cTrust.IsSended) { cTrust.Price = limitPrice; //支援可以下出 NextBar 的限價單(沒有指定會以 0 送出) __cNextBarRequires.Add(name); //標記 NextBar 時, 可以下單 return true; } return false; } else { if (cTrust.Price == limitPrice) { //委託價格一樣就忽略 return false; } else { if (cTrust.IsTrusted && !cTrust.IsCancel) { //如果已經委託完成就取消單號 SendTrust(cTrust, cTrust.Ticket); //向下單機傳送取消委託單的命令 cTrust.IsCancel = true; } return false; } } } TradeOrder cOrder = new TradeOrder(); //建立預約委託單的資訊 cOrder.Action = action; cOrder.BarNumber = Bars.CurrentBar; cOrder.Category = category; cOrder.Contracts = lots; cOrder.Name = name; cOrder.Price = limitPrice; cOrder.IsReverse = isReverse; cOrder.SymbolId = GetOrderSymbol(); cOrder.Time = DateTime.Now; cOrder.Ticket = (openNextBar) ? name : GetTrustID(); __cEntrusts.Add(cOrder); //加入至委託列表內 if (openNextBar) { //如果需要在下一根 Bars 下單, 就先保留 name 在佇列, 以方便比對委託單 __cReserves.Enqueue(name); __cNextBarRequires.Add(name); } else { if (__cCloseOrder == null) { SendTrust(cOrder); //傳送新委託單給下單機 } } return true; }
internal void SendTrust(TradeOrder trust, string cancelTicket = null) { EOrderAction cAction = trust.Action; bool bClose = cAction == EOrderAction.Sell || cAction == EOrderAction.BuyToCover; StringBuilder cBuilder = new StringBuilder(128); cBuilder.Append(__sLocalPipe).Append(",") .Append(trust.SymbolId).Append(",") .Append(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")).Append(",") .Append((cAction == EOrderAction.Buy || cAction == EOrderAction.BuyToCover) ? 'B' : (cAction == EOrderAction.Sell || cAction == EOrderAction.SellShort) ? 'S' : ' ').Append(",") .Append(trust.Contracts).Append(",") .Append(trust.Price).Append(",") .Append((cancelTicket != null) ? -1 : (bClose) ? 0 : 1).Append(",") .Append(Bars.Close[0]).Append(",") .Append((cancelTicket == null) ? (trust.IsReverse) ? "1" : "0" : cancelTicket); string sCommand = cBuilder.ToString(); __cPipeStream.Send(__sApiPipe, sCommand); //發送委託單命令給下單機 if (logger.IsInfoEnabled) logger.InfoFormat("[RealOrderService.Send] {0}", sCommand); trust.IsSended = true; //設定已經傳送完畢 if (bClose) { //如果是平倉單 __cCloseOrder = trust; //指定平倉單至變數內(模組會監測平倉單完畢後才會啟動之後的單做下單動作, 避免倉部位錯亂) } }
public override void OnWork() { if (Bars.IsLastBars) { if (Bars.CurrentBar > __iPrevious) { SendNextBars(__iPrevious); //處理 NextBars 下單程序 __iPrevious = Bars.CurrentBar; } HashSet<string> cSwap = __cPNextBarRequires; //將上一個條件狀態保留起來 __cPNextBarRequires = __cNextBarRequires; //將目前條件狀態置換到上一個條件狀態變數內 __cNextBarRequires = cSwap; //將上一個狀態置換到目前狀態上(減少使用 new 而造成 GC 負擔) __cNextBarRequires.Clear(); //清除狀態並等待下一輪 CalcBar 時儲存新條件狀態 if (__cCloseOrder != null && (__cCloseOrder.IsCancel || __cCloseOrder.Contracts == 0)) { bool bClear = true; int iCount = __cEntrusts.Count; if (iCount > 0) { bool bCancel = __cCloseOrder.IsCancel; for (int i = iCount - 1; i >= 0; i--) { //迴圈由最後一筆往前取得(避免 GetWaiting 取出後刪單造成取得順序問題) TradeOrder cTemp = __cEntrusts[i]; if (bCancel && !cTemp.IsSended) { //如果目前的平倉單被取消, 而且此單在倉閘內尚未傳送 GetWaiting(cTemp.SymbolId, cTemp.Price); //直接從委託倉閘內取出(放棄此單, 因為尚未送出可以直接取出並放棄) continue; } else if (!cTemp.IsSended && cTemp.Ticket != cTemp.Name) { //如果此單尚未送出而且 Ticket 與 Name 都不同(相同表示可能是下 NextBar 單而且尚未傳送, 這些單還不能傳送) SendTrust(cTemp); } if (bClear) { bClear = cTemp.IsTrusted && cTemp.IsDealed; //平倉完成後需要檢查委託倉內是否全部的單都有成交動作(如果有反向單則成交了才能改變倉位方向, 所以必須檢查是否全部委託單是否已經開始成交) } } } if (bClear) { //如果所有的單都已經開始成交或是已經無單, 表示可以清除此平倉變數 __cCloseOrder = null; } } if (__cCloseOrder == null) { //沒有平倉內容才可以計算成交部位(這樣倉位方向才不會算錯) CalculatePositions(); //計算成交部位狀況 } AsyncCalculateProfits(); //計算留倉部位損益 } }
private void SetTax(TradeOrder deal) { double dTotals = deal.Price * __cProperty.BigPointValue * deal.Contracts; ITax cTax = __cProperty.TaxRule as ITax; deal.Tax = cTax.GetTax(dTotals); }
private void SendTrust(TradeOrder trust, double dealPrice = 0) { trust.IsTrusted = true; if (logger.IsInfoEnabled) logger.InfoFormat("[Trust] #{0} {1} {2} {3} at {4} {5} @{6}", trust.Ticket, trust.SymbolId, trust.Action, trust.Contracts, trust.Price, trust.Name, trust.Time.ToString("yyyy-MM-dd HH:mm:ss")); if (trust.Price == 0) { //市價成交 OrderDeal(trust, dealPrice); } else { SendLimit(trust); } }
private void SendLimit(TradeOrder trust) { double dDealPrice = 0, dPrice = trust.Price; EOrderAction cAction = trust.Action; if (cAction == EOrderAction.Buy || cAction == EOrderAction.BuyToCover) { dDealPrice = (dPrice >= Bars.Close[0]) ? Bars.Close[0] : (dPrice >= Bars.Low[0]) ? dPrice : 0; } else { dDealPrice = (Bars.Close[0] >= dPrice) ? Bars.Close[0] : (Bars.High[0] >= dPrice) ? dPrice : 0; } if (dDealPrice > 0) { OrderDeal(trust, dDealPrice); } }
private void OrderDeal(TradeOrder trust, double dealPrice = 0) { trust.IsDealed = true; TradeOrder cDeal = trust.Clone(); cDeal.Price = (dealPrice == 0) ? Bars.Close[0] : dealPrice; cDeal.Time = Bars.Time[0]; SetTax(cDeal); double[] dValues = CalculateCommissions(cDeal); //計算交易佣金與手續費用(由策略使用者自行決定的佣金與手續費設定所計算出來的價格) cDeal.OtherFees = dValues[0]; cDeal.Fee = dValues[1]; __cDeals.Enqueue(cDeal); }
private bool CheckTrust(EOrderAction action, OrderCategory category, double limitPrice, int lots, string name, bool isReverse, bool openNextBar) { TradeOrder cTrust = __cEntrusts.GetTradeFromName(name); if (cTrust != null) { if (openNextBar) { __cNextBarRequires.Add(name); //標記 NextBar 時, 可以下單 return true; } else { if (cTrust.Price == limitPrice) { //委託價格一樣就忽略 return false; } else { if (cTrust.IsTrusted) { //如果已經委託完成就取消單號 string sTicket = cTrust.Ticket; //委託單號 AdjustClosedContract(cTrust); //調整平倉數量(如果是平倉單就要調整) __cEntrusts.Remove(cTrust.Ticket); //從委託陣列中移除 } else { //如果還沒有委託成功 return false; //直接離開(可能需要等到委託成功之後才能處理) } } } } if (action == EOrderAction.Sell || action == EOrderAction.BuyToCover) { int iLots = __cCurrentPosition.OpenLots - __iUsedClosedTempLots; //檢查平倉數量是否足夠 if (lots > iLots) { //平倉數量不足(不下單) return false; } else { lock (__oTempLock) { __iUsedClosedTempLots += lots; } } } TradeOrder cOrder = new TradeOrder(); //建立預約委託單的資訊 cOrder.Action = action; cOrder.BarNumber = Bars.CurrentBar; cOrder.Category = category; cOrder.Contracts = lots; cOrder.Name = name; cOrder.Price = limitPrice; cOrder.IsReverse = isReverse; cOrder.SymbolId = Bars.Request.Symbol; cOrder.Time = Bars.Time[0]; cOrder.Ticket = (openNextBar) ? name : GetTrustID(); __cEntrusts.Add(cOrder); //加入至委託列表內 if (openNextBar) { //如果需要在下一根 Bars 下單, 就先保留 name 在佇列, 以方便比對委託單 __cReserves.Enqueue(name); __cNextBarRequires.Add(name); } else { if (isReverse) { //如果有反轉倉單 CancelLimit(action); //取消所有反向之前的限價委託單 } SendTrust(cOrder); //傳送新委託單給下單機 } return true; }
private void AdjustClosedContract(TradeOrder order) { EOrderAction cAction = order.Action; if (cAction == EOrderAction.Sell || cAction == EOrderAction.BuyToCover) { lock (__oTempLock) { if (__iUsedClosedTempLots > 0) { __iUsedClosedTempLots -= order.Contracts; } } } }