/// <summary> /// this must be called once per position tracker, for each position update. /// if you are using your own position tracker with this trailing stop(eg from offset tracker, or somewhere else) /// you MUST call TrailTrackers Adjust and NOT call your position tracker's adjust /// </summary> /// <param name="fill"></param> public void Adjust(Trade fill) { // get index for symbol int idx = symidx(fill.FullSymbol); // only do following if we're tracking trail for this symbol if (idx != NOSYM) { // get current position size int psize = _pt[fill.FullSymbol].UnsignedSize; // get trailing information OffsetInfo trail = _trail[idx]; // get actual position size after change int asize = psize - fill.UnsignedSize; // if expected and actual match, mark pending as false if (esize[fill.FullSymbol] == asize) { D(fill.FullSymbol + " trailing stop completely filled with: " + fill.ToString()); _pendingfill[idx] = false; } else v(fill.FullSymbol + " trail partial fill: " + fill.ToString() + " e: " + esize[fill.FullSymbol] + " != a: " + asize); } else v(fill.FullSymbol + " fill: " + fill.ToString() + " ignored while trail disabled."); _pt.Adjust(fill); // if we're flat now, make sure ref price is reset if (_pt[fill.FullSymbol].isFlat) { _ref[idx] = 0; v(fill.FullSymbol + " flat, reset trail reference price."); } }
public static string Serialize(OffsetInfo oi) { string m = string.Format("{0},{1},{2},{3},{4},{5}", oi.ProfitDist, oi.StopDist, oi.ProfitPercent, oi.StopPercent, oi.NormalizeSize, oi.MinimumLotSize); return m; }
OffsetInfo GetOffset(string sym) { // see if we have a custom offset int idx = getindex(sym); OffsetInfo oi = null; if (idx >= 0) { oi = base[idx]; // verify it's valid if (oi == null) { idx = -1; } } // if we don't have a custom but we're adding one, add from default if (AddCustom && (idx < 0)) { idx = addindex(sym, DefaultOffset); } else if (oi == null) // otherwise use default { return(DefaultOffset); } // return custom return(oi); }
public static string Serialize(OffsetInfo oi) { string m = string.Format("{0},{1},{2},{3},{4},{5}", oi.ProfitDist, oi.StopDist, oi.ProfitPercent, oi.StopPercent, oi.NormalizeSize, oi.MinimumLotSize); return(m); }
void SetOffset(string sym, OffsetInfo off) { // check for index int idx = addindex(sym, off); if (_verbdebug) { debug(sym + " set offset: " + off.ToString(DebugDecimals)); } }
void SetOffset(string sym, OffsetInfo off) { // check for index if (_trackedOffsetInfos.ContainsKey(sym)) { _trackedOffsetInfos[sym] = off; } else { _trackedOffsetInfos.Add(sym, off); } if (_verbdebug) { debug(sym + " set offset: " + off.ToString(DebugDecimals)); } }
/// <summary> /// must send new positions here (eg from GotPosition on Response) /// </summary> /// <param name="p"></param> public void Adjust(Position p) { // did position exist? bool exists = !_pt[p.FullSymbol].isFlat; if (exists) { debug(p.FullSymbol + " re-initialization of existing position"); } if (exists && ShutdownOnReinit) { // get offset OffsetInfo oi = GetOffset(p.FullSymbol); // disable it oi.ProfitPercent = 0; oi.StopPercent = 0; // save it SetOffset(p.FullSymbol, oi); // cancel existing orders CancelAll(p.FullSymbol); // stop processing return; } // update position _pt.Adjust(p); // if we're flat, nothing to do if (_pt[p.FullSymbol].isFlat) { debug(p.FullSymbol + " initialized to flat."); // cancel pending offsets CancelAll(p.FullSymbol); // reset offset state but not configuration SetOffset(p.FullSymbol, new OffsetInfo(this[p.FullSymbol])); return; } // do we have events? if (!HasEvents()) { return; } // do update doupdate(p.FullSymbol); }
OffsetInfo GetOffset(string sym) { // see if we have a custom offset OffsetInfo oi = null; if (_trackedOffsetInfos.ContainsKey(sym)) { oi = _trackedOffsetInfos[sym]; if (oi == null) { _trackedOffsetInfos[sym] = DefaultOffset; oi = DefaultOffset; } } else { oi = DefaultOffset; _trackedOffsetInfos.Add(sym, oi); } return(oi); }
void cancel(OffsetInfo offset) { bool hit = false; string ids = string.Empty; if (offset.hasProfit) { hit |= true; ids += offset.ProfitId + " "; cancel(offset.ProfitId); } if (offset.hasStop) { hit |= true; ids += offset.StopId + " "; cancel(offset.StopId); } if (hit) { debug("canceling offsets: " + ids); } }
/// <summary> /// clear single custom offset /// </summary> /// <param name="sym"></param> public void ClearCustom(string sym) { this[sym] = IgnoreDefault ? OffsetInfo.DISABLEOFFSET() : DefaultOffset; }
/// <summary> /// copy an existing offset to this one /// </summary> /// <param name="copy"></param> public OffsetInfo(OffsetInfo copy) : this(copy.ProfitDist, copy.StopDist, copy.ProfitPercent, copy.StopPercent, copy.NormalizeSize, copy.MinimumLotSize) { }
/// <summary> /// get a stop order for a position given offset information /// </summary> /// <param name="p"></param> /// <param name="offset"></param> /// <returns></returns> public static Order PositionStop(Position p, OffsetInfo offset) { return(PositionStop(p, offset.StopDist, offset.StopPercent, offset.NormalizeSize, offset.MinimumLotSize)); }
/// <summary> /// get profit order for given position given offset information /// </summary> /// <param name="p"></param> /// <param name="offset"></param> /// <returns></returns> public static Order PositionProfit(Position p, OffsetInfo offset) { return(PositionProfit(p, offset.ProfitDist, offset.ProfitPercent, offset.NormalizeSize, offset.MinimumLotSize)); }
void SetOffset(string sym, OffsetInfo off) { // check for index if (_trackedOffsetInfos.ContainsKey(sym)) _trackedOffsetInfos[sym] = off; else _trackedOffsetInfos.Add(sym, off); if (_verbdebug) debug(sym + " set offset: " + off.ToString(DebugDecimals)); }
/// <summary> /// get profit order for given position given offset information /// </summary> /// <param name="p"></param> /// <param name="offset"></param> /// <returns></returns> public static Order PositionProfit(Position p, OffsetInfo offset) { return PositionProfit(p, offset.ProfitDist, offset.ProfitPercent, offset.NormalizeSize, offset.MinimumLotSize); }
/// <summary> /// pass arbitrary price to use for trail reference price /// </summary> /// <param name="symbol"></param> /// <param name="p"></param> public void newPoint(string symbol, decimal p) { // get index for symbol int idx = symidx(symbol); // setup parameters OffsetInfo trail = null; decimal refp = 0; // see if we trail this symbol if ((idx == NOSYM) && _trailbydefault) { // get parameters idx = _trail.Count; refp = p; trail = new OffsetInfo(_defaulttrail); // save them _symidx.Add(symbol, idx); _ref.Add(refp); _pendingfill.Add(false); firecount.Add(symbol, 0); esize.Add(symbol, pt[symbol].UnsignedSize); // just in case user is modifying on seperate thread lock (_trail) { _trail.Add(trail); } D(symbol + " trail tracking modified: " + trail.ToString()); } else if ((idx == NOSYM) && !_trailbydefault) { return; } else { // get parameters refp = _ref[idx]; // in case tracker started after trail stop should have been broken. if (refp == 0 && _pt[symbol].isValid) { refp = _pt[symbol].AvgPrice; } // just in case user tries to modify on seperate thread lock (_trail) { trail = _trail[idx]; } } // see if we need to update ref price if ((refp == 0) || (_pt[symbol].isLong && (refp < p)) || (_pt[symbol].isShort && (refp > p))) { // update refp = p; // save it _ref[idx] = refp; // notify v(symbol + " new reference price: " + p); } // see if we broke our trail var testdist = Math.Abs(refp - p); var trailtest = testdist > trail.StopDist; if (!_pendingfill[idx] && (trail.StopDist != 0) && trailtest && (MaxFireCount > firecount[symbol])) { // notify D(symbol + " hit trailing stop at: " + p.ToString("F2")); // mark pending order _pendingfill[idx] = true; // get order Order flat = new MarketOrderFlat(_pt[symbol], trail.StopPercent, trail.NormalizeSize, trail.MinimumLotSize); // get order id flat.id = _id.AssignId; // adjust expectation esize[symbol] -= flat.UnsignedSize; // count fire firecount[symbol]++; // send flat order SendOrder(flat); // notify D(symbol + " enforcing trail with: " + flat.ToString() + " esize: " + esize[symbol] + " count: " + firecount[symbol]); if (HitOffset != null) HitOffset(symbol, flat.id, p); } else if (!_noverb) { if (_pendingfill[idx]) v(symbol + " waiting for trail fill."); else if (trail.StopDist == 0) v(symbol + " trail has been disabled."); else if (!trailtest) { v(symbol + " trail not hit, current dist: " + testdist + " trailamt: " + trail.StopDist); } else if (MaxFireCount > firecount[symbol]) { v(symbol + " trail max fire reached at: " + firecount[symbol] + " max: " + MaxFireCount); } } }
void doupdate(string sym) { // is update ignored? if (IgnoreUpdate(sym)) { return; } // wait till next tick if we send cancels bool sentcancel = false; // get our offset values OffsetInfo off = GetOffset(sym); // get position Position p = new Position(_pt[sym]); // if we're flat, nothign to do if (p.isFlat) { return; } // see whats current bool cprofit = off.isProfitCurrent(p); bool cstop = off.isStopCurrent(p); // if not current, mark for update bool updateprofit = !cprofit; bool updatestop = !cstop; // if we're up to date then quit if (!updatestop && !updateprofit) { return; } // see if we have stop update if ((updatestop && off.hasStop && !CancelOnce) || (updatestop && off.hasStop && CancelOnce && !off.StopcancelPending)) { // notify if (!off.StopcancelPending) { debug(string.Format("attempting stop cancel: {0} {1}", sym, off.StopId)); } // cancel existing stops cancel(off.StopId); // mark cancel pending off.StopcancelPending = true; // mark as sent sentcancel |= true; } // see if we have profit update if ((updateprofit && off.hasProfit && AllowSimulatenousCancels) || (updateprofit && off.hasProfit && AllowSimulatenousCancels && !sentcancel)) { if (!CancelOnce || (CancelOnce && !off.ProfitcancelPending)) { // notify if (!off.ProfitcancelPending) { debug(string.Format("attempting profit cancel: {0} {1}", sym, off.ProfitId)); } // cancel existing profits cancel(off.ProfitId); // mark cancel pending off.ProfitcancelPending = true; // mark as sent sentcancel |= true; } } // wait till next tick if we sent cancel if (sentcancel && WaitAfterCancel) { return; } bool sentorder = false; // send stop first if (!off.hasStop) { // since we have no stop, it's cancel can't be pending off.StopcancelPending = false; // get new stop Order stop = Calc.PositionStop(p, off.StopDist, off.StopPercent, off.NormalizeSize, off.MinimumLotSize); // mark size off.SentStopSize = stop.OrderSize; // if it's valid, send and track if (stop.IsValid) { stop.Id = Ids.NextOrderId; off.StopId = stop.Id; SendOrderEvent(stop); // notify debug(string.Format("sent new stop: {0} {1}", stop.Id, stop.ToString(DebugDecimals))); sentorder = true; } else if (_verbdebug) { debug(sym + " invalid stop: " + stop.ToString(DebugDecimals)); } } if ((!off.hasProfit && AllowSimulatenousOrders) || (!off.hasProfit && !AllowSimulatenousOrders && !sentorder)) { // since we have no stop, it's cancel can't be pending off.ProfitcancelPending = false; // get new profit Order profit = Calc.PositionProfit(p, off.ProfitDist, off.ProfitPercent, off.NormalizeSize, off.MinimumLotSize); // mark size off.SentProfitSize = profit.OrderSize; // if it's valid, send and track it if (profit.IsValid) { profit.Id = Ids.NextOrderId; off.ProfitId = profit.Id; SendOrderEvent(profit); // notify debug(string.Format("sent new profit: {0} {1}", profit.Id, profit.ToString(DebugDecimals))); sentorder = true; } else if (_verbdebug) { debug(sym + " invalid profit: " + profit.ToString(DebugDecimals)); } } // make sure new offset info is reflected SetOffset(sym, off); }
/// <summary> /// get a stop order for a position given offset information /// </summary> /// <param name="p"></param> /// <param name="offset"></param> /// <returns></returns> public static Order PositionStop(Position p, OffsetInfo offset) { return PositionStop(p, offset.StopDist, offset.StopPercent, offset.NormalizeSize, offset.MinimumLotSize); }
void cancel(OffsetInfo offset) { bool hit = false; string ids = string.Empty; if (offset.hasProfit) { hit |= true; ids += offset.ProfitId + " "; cancel(offset.ProfitId); } if (offset.hasStop) { hit |= true; ids += offset.StopId + " "; cancel(offset.StopId); } if (hit) debug("canceling offsets: " + ids); }
/// <summary> /// must send new fills here (eg call from Response.GotFill) /// </summary> /// <param name="t"></param> public void Adjust(Trade t) { // get original size int osize = _pt[t.FullSymbol].Size; // update position _pt.Adjust(t); // see if it's our order OffsetInfo oi = GetOffset(t.FullSymbol); // see what was hit bool hp = (t.Id != 0) && (oi.ProfitId == t.Id); bool hs = (t.Id != 0) && (oi.StopId == t.Id); // if we hit something if (hp || hs) { // notify debug(t.FullSymbol + " hit " + (hp ? "profit" : "stop") + ": " + t.Id); // see if we should clear offset if (hp && (oi.SentProfitSize == t.TradeSize)) { debug(t.FullSymbol + " profit closed: " + t.Id); oi.ProfitId = 0; } else if (hp) { oi.SentProfitSize -= t.TradeSize; } if (hs && (oi.SentStopSize == t.TradeSize)) { debug(t.FullSymbol + " stop closed: " + t.Id); oi.StopId = 0; } else if (hs) { oi.SentStopSize -= t.TradeSize; } if (HitOffset != null) { HitOffset(t.FullSymbol, t.Id, t.TradePrice); } } // if we're flat, nothing to do (or if we switched sides) Position p = _pt[t.FullSymbol]; if (p.isFlat || (osize * p.Size < -1)) { if (p.isFlat) { debug(t.FullSymbol + " now flat."); } else { debug(t.FullSymbol + " reversed: " + osize + " -> " + p.Size); } CancelAll(t.FullSymbol); // reset offset state but not configuration SetOffset(t.FullSymbol, new OffsetInfo(this[t.FullSymbol])); } else // save offset { SetOffset(t.FullSymbol, oi); } // do we have events? if (!HasEvents()) { return; } // do update doupdate(t.FullSymbol); }
void SetOffset(string sym, OffsetInfo off) { // check for index int idx = addindex(sym, off); if (_verbdebug) debug(sym + " set offset: " + off.ToString(DebugDecimals)); }