Archive defaults: BTCUSDT · 4h decisions · 4h execution. Uses the current chart symbol and timeframe.
Regenerated from the installed original archive. TradingView verification on 10,140 identical original candles, including seed history, matched trade decisions and quantities, with equity within 0.0000001. The Archive rules table and native fill markers use loaded chart candles with the archive calculation and account rules. Use the archived execution timeframe and the same candle history from the same origin to compare results. Changed charts or inputs are fresh exploratory runs. Optional Strategy Tester orders approximate sizing and percentage slippage; broker settings are separate from the Archive account inputs.
strategy("VESTROS C10919-LONG: archive rules", overlay = true, initial_capital = 10000, default_qty_type = strategy.percent_of_equity, default_qty_value = 100, commission_type = strategy.commission.percent, commission_value = 0.08, slippage = 0, pyramiding = 100, margin_long = 0, margin_short = 0, process_orders_on_close = false, calc_on_every_tick = false, calc_on_order_fills = false, max_bars_back = 5000)f_gt(float a, float b) => math.sign(a - b) == 1f_lt(float a, float b) => math.sign(a - b) == -1f_eq(float a, float b) => math.sign(a - b) == 0f_ge(float a, float b) => not na(a) and not na(b) and math.sign(a - b) >= 0f_le(float a, float b) => not na(a) and not na(b) and math.sign(a - b) <= 0f_number(bool value) => value ? 1.0 : 0.0type Samples array<float> valuesf_samples() => Samples.new(array.new<float>())method add(Samples self, float value, int capacity = 82) => self.values.push(value) if self.values.size() > capacity self.values.shift()method lag(Samples self, int offset = 0) => self.values.size() > offset ? self.values.get(self.values.size() - 1 - offset) : namethod stats(Samples self, int length, int offset = 0) => float average = na float deviation = na if self.values.size() >= length + offset float total = 0 for i = length - 1 to 0 total += self.lag(i + offset) average := total / length float squares = 0 for i = length - 1 to 0 float delta = self.lag(i + offset) - average squares += delta * delta deviation := math.sqrt(squares / length) [average, deviation]method zscore(Samples self, int length) => [average, deviation] = self.stats(length) (self.lag() - average) / deviationmethod volatility(Samples self, int length, int offset = 1) => [average, deviation] = self.stats(length, offset) deviationmethod rangePosition(Samples self, int length) => float minimum = self.lag(1) float maximum = self.lag(1) for i = 2 to length minimum := math.min(minimum, self.lag(i)) maximum := math.max(maximum, self.lag(i)) 2 * (self.lag() - minimum) / (maximum - minimum) - 1method rank(Samples self, int length) => float current = self.lag() bool complete = not na(current) int less = 0 int equal = 0 for i = 1 to length float prior = self.lag(i) complete := complete and not na(prior) less += f_lt(prior, current) ? 1 : 0 equal += f_eq(prior, current) ? 1 : 0 complete ? 2.0 * (less + equal / 2.0) / length - 1 : namethod persistence(Samples self, int length, float threshold) => bool complete = true bool passed = true for i = 0 to length - 1 float value = self.lag(i) complete := complete and not na(value) passed := passed and (threshold >= 0 ? f_gt(value, threshold) : f_lt(value, threshold)) complete ? f_number(passed) : naf_relative(Samples left, Samples right, int length) => (left.lag() - left.lag(length)) / math.abs(left.lag(length)) - (right.lag() - right.lag(length)) / math.abs(right.lag(length))f_cross(Samples left, Samples right, bool above) => bool complete = not na(left.lag()) and not na(right.lag()) and not na(left.lag(1)) and not na(right.lag(1)) complete ? f_number(above ? f_gt(left.lag(), right.lag()) and f_le(left.lag(1), right.lag(1)) : f_lt(left.lag(), right.lag()) and f_ge(left.lag(1), right.lag(1))) : naf_compare(float left, float right, string comparison) => not na(left) and not na(right) ? f_number(comparison == "gt" ? f_gt(left, right) : comparison == "lt" ? f_lt(left, right) : comparison == "ge" ? f_ge(left, right) : f_le(left, right)) : naf_votes(array<float> values, int required) => int passed = 0 int unknown = 0 for value in values passed += value == 1 ? 1 : 0 unknown += na(value) ? 1 : 0 passed >= required ? 1.0 : passed + unknown < required ? 0.0 : namethod riskScale(Samples self) => Samples returns = f_samples() bool positive = true for i = 80 to 1 float previous = self.lag(i) float current = self.lag(i - 1) positive := positive and f_gt(previous, 0) and f_gt(current, 0) returns.add(current / previous - 1) [slowMean, slow] = returns.stats(60, 20) [fastMean, fast] = returns.stats(20) positive and f_gt(slow, 0) and f_gt(fast, 0) ? math.min(1, math.max(0.25, slow / fast)) : naf_histories(int count) => array<Samples> result = array.new<Samples>() for i = 1 to count result.push(f_samples()) resultf_median(array<float> values) => array<float> ordered = values.copy() ordered.sort() int middle = int(math.floor(ordered.size() / 2)) float lower = ordered.get(math.max(0, middle - 1)) float upper = ordered.get(middle) ordered.size() % 2 == 1 ? upper : f_lt(lower, 0) and f_gt(upper, 0) ? lower / 2 + upper / 2 : lower + (upper - lower) / 2method priorMad(Samples self, int length) => array<float> prior = array.new<float>() bool complete = not na(self.lag()) for i = length to 1 float value = self.lag(i) complete := complete and not na(value) prior.push(value) float result = na if complete float center = f_median(prior) array<float> deviations = array.new<float>() for value in prior deviations.push(math.abs(value - center)) result := (self.lag() - center) / (1.482602218505602 * f_median(deviations)) resultmethod downsideShare(Samples self, int length) => float downside = 0 float total = 0 bool complete = true for i = length to 1 float previous = self.lag(i + 1) float current = self.lag(i) complete := complete and f_gt(previous, 0) and f_gt(current, 0) float change = math.log(current) - math.log(previous) float square = change * change total += square if f_lt(change, 0) downside += square complete ? downside / total : naf_pairStats(Samples left, Samples right, int length, int offset) => [leftMean, leftDeviation] = left.stats(length, offset) [rightMean, rightDeviation] = right.stats(length, offset) float covariance = 0 float leftVariance = 0 float rightVariance = 0 for i = length - 1 to 0 float leftDelta = left.lag(i + offset) - leftMean float rightDelta = right.lag(i + offset) - rightMean covariance += leftDelta * rightDelta leftVariance += leftDelta * leftDelta rightVariance += rightDelta * rightDelta [leftMean, rightMean, covariance, leftVariance, rightVariance]f_correlation(Samples left, Samples right, int length) => [leftMean, rightMean, covariance, leftVariance, rightVariance] = f_pairStats(left, right, length, 0) covariance / math.sqrt(leftVariance * rightVariance)f_regression(Samples left, Samples right, int length) => [leftMean, rightMean, covariance, leftVariance, rightVariance] = f_pairStats(left, right, length, 1) float beta = covariance / leftVariance right.lag() - (rightMean - beta * leftMean + beta * left.lag())f_region(float value, float lower, float upper) => na(value) or na(lower) or na(upper) ? na : f_gt(value, upper) ? 1.0 : f_lt(value, lower) ? -1.0 : 0.0f_breakout(Samples price, Samples lower, Samples upper) => float current = f_region(price.lag(), lower.lag(), upper.lag()) float previous = f_region(price.lag(1), lower.lag(1), upper.lag(1)) na(current) ? na : na(previous) ? 0.0 : current == 1 and previous != 1 ? 1.0 : current == -1 and previous != -1 ? -1.0 : 0.0type AtrRatio float previousClose = na float atr = na float seedSum = 0 int count = 0method calculate(AtrRatio self, float highPrice, float lowPrice, float closePrice, int period) => float trueRange = na(self.previousClose) ? highPrice - lowPrice : math.max(highPrice - lowPrice, math.abs(highPrice - self.previousClose), math.abs(lowPrice - self.previousClose)) float result = trueRange / self.atr if na(self.atr) self.seedSum += trueRange self.count += 1 if self.count == period self.atr := self.seedSum / period else self.atr := (self.atr * (period - 1) + trueRange) / period self.previousClose := closePrice resulttype OpeningRange int sessionHour int sessionMinute string sessionTimeZone float openingRangeHigh = na float openingRangeLow = na float openingRangeMid = na bool isOpeningRangeBar = falsemethod calculate(OpeningRange self, float highPrice, float lowPrice, int openAt, int closeAt) => int target = self.sessionHour * 60 + self.sessionMinute int firstMinute = hour(openAt, self.sessionTimeZone) * 60 + minute(openAt, self.sessionTimeZone) int lastMinute = hour(closeAt - 1, self.sessionTimeZone) * 60 + minute(closeAt - 1, self.sessionTimeZone) self.isOpeningRangeBar := closeAt - openAt >= 86400000 or (lastMinute >= firstMinute ? target >= firstMinute and target <= lastMinute : target >= firstMinute or target <= lastMinute) if self.isOpeningRangeBar self.openingRangeHigh := highPrice self.openingRangeLow := lowPrice self.openingRangeMid := (highPrice + lowPrice) / 2type Holding int steps = 0 float average = 0 bool pending = false int targetDirection = 0 float allocation = 1 float executedDelta = 0 float executedPrice = na float markedPrice = na float referencePrice = na float plannedPrice = 0type Account float initial float wallet float equity float peak float drawdown = 0 float fees = 0 float slippage = 0 int fills = 0 int closed = 0 array<Holding> holdings string mode = "combination"f_account(float capital, int routes = 1, string mode = "combination") => array<Holding> holdings = array.new<Holding>() for i = 1 to routes holdings.push(Holding.new(allocation = 1.0 / routes)) Account.new(capital, capital, capital, capital, holdings = holdings, mode = mode)method valueAt(Account self, float price, float quantityStep) => float value = self.wallet for holding in self.holdings value += holding.steps * quantityStep * (price - holding.average) valuemethod observe(Account self, float quantityStep) => self.equity := self.wallet for holding in self.holdings if holding.steps != 0 self.equity += holding.steps * quantityStep * (holding.markedPrice - holding.average) self.peak := math.max(self.peak, self.equity) self.drawdown := math.max(self.drawdown, (self.peak - self.equity) / self.peak)method mark(Account self, float price, float quantityStep) => for holding in self.holdings holding.markedPrice := price self.observe(quantityStep)method execute(Account self, float openPrice, float quantityStep, float feeRate, float slipRate) => float sizingEquity = self.valueAt(openPrice, quantityStep) for holding in self.holdings holding.executedDelta := 0 holding.executedPrice := na if holding.pending float sizingPrice = openPrice float sizingNotional = math.max(sizingEquity, 0) * holding.allocation if self.mode == "managed" float reserved = 0 for other in self.holdings reserved += math.abs(other.steps) * quantityStep * other.plannedPrice sizingPrice := holding.referencePrice sizingNotional := math.min(math.max(self.equity, 0) * holding.allocation, math.max(self.equity - reserved, 0)) int target = holding.targetDirection == 0 ? 0 : holding.targetDirection * int(math.floor(sizingNotional / (sizingPrice * quantityStep))) int before = holding.steps int deltaSteps = target - before if deltaSteps != 0 float delta = deltaSteps * quantityStep float fill = openPrice * (deltaSteps > 0 ? 1 + slipRate : 1 - slipRate) float fee = math.abs(delta) * fill * feeRate self.wallet -= fee self.fees += fee self.slippage += math.abs(delta) * math.abs(fill - openPrice) self.fills += 1 holding.executedDelta := delta holding.executedPrice := fill if before == 0 or math.sign(before) == math.sign(deltaSteps) holding.average := (math.abs(before) * quantityStep * holding.average + math.abs(delta) * fill) / (math.abs(target) * quantityStep) else int closing = math.min(math.abs(before), math.abs(deltaSteps)) self.wallet += closing * quantityStep * math.sign(before) * (fill - holding.average) if target == 0 or math.sign(target) != math.sign(before) self.closed += 1 holding.average := target == 0 ? 0 : fill holding.steps := target holding.markedPrice := fill if before == 0 or math.sign(before) != math.sign(target) holding.plannedPrice := target == 0 ? 0 : holding.referencePrice if self.mode == "combination" self.mark(fill, quantityStep) else if self.mode == "managed" self.observe(quantityStep) holding.pending := false if self.mode == "open" self.mark(openPrice, quantityStep)type Member bool active = false int enteredAt = na int heldBars = 0 bool changed = false float entryPrice = na float highestClose = na float lowestClose = na bool pendingReference = falsemethod fillReference(Member self, float openPrice, float slipRate, int direction) => if self.pendingReference self.entryPrice := openPrice * (direction == 1 ? 1 + slipRate : 1 - slipRate) self.pendingReference := falsemethod decide(Member self, bool entry, bool exit, int closeAt) => bool before = self.active if self.active self.heldBars += 1 if exit self.active := false self.enteredAt := na self.heldBars := 0 self.highestClose := na self.lowestClose := na self.entryPrice := na else if entry self.active := true self.enteredAt := closeAt self.heldBars := 0 self.pendingReference := true self.changed := self.active != beforetype Incumbent int member = 0 int inactive = 0 bool desired = false bool changed = falsemethod combine(Incumbent self, Member a, Member b) => bool event = a.changed or b.changed bool before = self.desired self.changed := false if event or (self.desired and self.member != 0 and not a.active and not b.active) if a.active or b.active bool keep = (self.member == 1 and a.active) or (self.member == 2 and b.active) if not keep self.member := a.active and b.active ? (a.enteredAt <= b.enteredAt ? 1 : 2) : a.active ? 1 : 2 self.inactive := 0 self.desired := true else if self.desired and self.member != 0 self.inactive += 1 if self.inactive >= 2 self.member := 0 self.desired := false else self.member := 0 self.inactive := 0 self.desired := false self.changed := event or self.desired != beforetype Program0 OpeningRange calculation array<Samples> history AtrRatio atrRatio bool entry = false bool exit = falsemethod features(Program0 self, float openPrice, float highPrice, float lowPrice, float closePrice, float volumeValue, bool available = true) => float n0 = available ? self.calculation.openingRangeHigh : na self.history.get(0).add(n0) float n1 = available ? self.calculation.openingRangeLow : na self.history.get(1).add(n1) float n2 = available ? self.calculation.openingRangeMid : na self.history.get(2).add(n2) float n3 = closePrice self.history.get(3).add(n3) float n4 = highPrice self.history.get(4).add(n4) float n5 = lowPrice self.history.get(5).add(n5) float n6 = -1.0 self.history.get(6).add(n6) float n7 = 0.0 self.history.get(7).add(n7) float n8 = 1.5 self.history.get(8).add(n8) float n9 = 1.0 self.history.get(9).add(n9) float n10 = n0 - n1 self.history.get(10).add(n10) float n11 = f_regression(self.history.get(0), self.history.get(2), 60) self.history.get(11).add(n11) float n12 = self.history.get(1).zscore(20) self.history.get(12).add(n12) float n13 = self.history.get(2).zscore(20) self.history.get(13).add(n13) float n14 = self.atrRatio.calculate(n4, n5, n3, 14) self.history.get(14).add(n14) float n15 = self.history.get(10).zscore(20) self.history.get(15).add(n15) float n16 = self.history.get(11).zscore(60) self.history.get(16).add(n16) float n17 = n13 - n12 self.history.get(17).add(n17) float n18 = f_compare(n14, n8, "ge") self.history.get(18).add(n18) float n19 = f_cross(self.history.get(15), self.history.get(9), true) self.history.get(19).add(n19) float n20 = f_cross(self.history.get(16), self.history.get(6), false) self.history.get(20).add(n20) float n21 = f_cross(self.history.get(16), self.history.get(9), true) self.history.get(21).add(n21) float n22 = f_cross(self.history.get(17), self.history.get(7), true) self.history.get(22).add(n22) float n23 = f_votes(array.from(n19, n21, n22), 2) self.history.get(23).add(n23) self.entry := n23 == 1 and n18 == 1 self.exit := n20 == 1method advance(Account account, Program0 a, Member aMember, float openPrice, float highPrice, float lowPrice, float closePrice, float volumeValue, int openAt, int closeAt, bool scored, bool mayDecide, float quantityStep, float feeRate, float slipRate) => aMember.changed := false if scored account.execute(openPrice, quantityStep, feeRate, slipRate) aMember.fillReference(openPrice, slipRate, 1) a.calculation.calculate(highPrice, lowPrice, openAt, closeAt) a.features(openPrice, highPrice, lowPrice, closePrice, volumeValue) if mayDecide if aMember.active aMember.highestClose := na(aMember.highestClose) ? closePrice : math.max(aMember.highestClose, closePrice) aMember.lowestClose := na(aMember.lowestClose) ? closePrice : math.min(aMember.lowestClose, closePrice) aMember.decide(a.entry, a.exit, closeAt) if aMember.changed Holding holding = account.holdings.get(0) holding.pending := true holding.targetDirection := aMember.active ? 1 : 0 if scored account.mark(closePrice, quantityStep)useWindow = input.bool(true, "Use archive date window", group = "Replay")startAt = input.time(1640995200000, "First scored open", group = "Replay")endAt = input.time(1782864000000, "End (exclusive)", group = "Replay")initialCapital = input.float(10000, "Initial equity", minval = 1, group = "Archive account")quantityStep = input.float(0.001, "Quantity step", minval = 0.000001, group = "Archive account")feeRate = input.float(0.06, "Fee (%)", minval = 0, group = "Archive account") / 100slipRate = input.float(0.02, "Slippage (%)", minval = 0, group = "Archive account") / 100showOrders = input.bool(false, "Show approximate broker orders", group = "Display")showNativeFills = input.bool(true, "Show native fill markers", group = "Display")scored = not useWindow or time >= startAt and time < endAtmayDecide = scored and (not useWindow or time_close < endAt)var int origin = timevar int scoredCount = 0sessionHour = input.int(9, "Session hour", minval = 0, maxval = 23, group = "Opening range")sessionMinute = input.int(30, "Session minute", minval = 0, maxval = 59, group = "Opening range")sessionTimeZone = input.string("America/New_York", "Session time zone", group = "Opening range")var OpeningRange aCalculation = OpeningRange.new(sessionHour, sessionMinute, sessionTimeZone)var Program0 a = Program0.new(aCalculation, f_histories(24), AtrRatio.new())var Member aMember = Member.new()var Account account = f_account(initialCapital, 1, "open")if barstate.isconfirmed account.advance(a, aMember, open, high, low, close, volume, time, time_close, scored, mayDecide, quantityStep, feeRate, slipRate) if scored scoredCount += 1 if showOrders and mayDecide float target = 0 bool change = false for holding in account.holdings target += holding.pending ? holding.targetDirection * math.floor(math.max(account.equity, 0) * holding.allocation / (close * quantityStep)) * quantityStep : holding.steps * quantityStep change := change or holding.pending float delta = target - strategy.position_size if change and f_gt(delta, 0) strategy.order("Buy delta", strategy.long, qty = delta) else if change and f_lt(delta, 0) strategy.order("Sell delta", strategy.short, qty = -delta)var table summary = table.new(position.top_right, 2, 8, bgcolor = color.new(color.black, 10))if barstate.islast table.cell(summary, 0, 0, "Archive rules", text_color = color.white) table.cell(summary, 1, 0, "Loaded chart candles", text_color = color.white) table.cell(summary, 0, 1, "Equity", text_color = color.white) table.cell(summary, 1, 1, str.tostring(account.equity, "#.########"), text_color = color.white) table.cell(summary, 0, 2, "Net return", text_color = color.white) table.cell(summary, 1, 2, str.tostring((account.equity / initialCapital - 1) * 100, "#.########") + "%", text_color = color.white) table.cell(summary, 0, 3, "Maximum drawdown", text_color = color.white) table.cell(summary, 1, 3, str.tostring(account.drawdown * 100, "#.########") + "%", text_color = color.white) table.cell(summary, 0, 4, "Closed trades / fills", text_color = color.white) table.cell(summary, 1, 4, str.tostring(account.closed) + " / " + str.tostring(account.fills), text_color = color.white) table.cell(summary, 0, 5, "Fees", text_color = color.white) table.cell(summary, 1, 5, str.tostring(account.fees, "#.########"), text_color = color.white) table.cell(summary, 0, 6, "Calculation origin", text_color = color.white) table.cell(summary, 1, 6, str.format_time(origin, "yyyy-MM-dd HH:mm", "UTC"), text_color = color.white) table.cell(summary, 0, 7, "Scored candles", text_color = color.white) table.cell(summary, 1, 7, str.tostring(scoredCount), text_color = color.white)plot(account.equity, "native_equity", display = display.data_window)plot(account.fees, "native_fees", display = display.data_window)plot(account.drawdown, "native_drawdown", display = display.data_window)plot(account.fills, "native_fills", display = display.data_window)plot(account.closed, "native_closed", display = display.data_window)plot(aMember.active ? 1 : 0, "native_a_active", display = display.data_window)plot(account.holdings.get(0).steps * quantityStep, "native_quantity_0", display = display.data_window)plotshape(showNativeFills and scored and barstate.isconfirmed and (f_gt(account.holdings.get(0).executedDelta, 0)), "Native buy fill", shape.triangleup, location.belowbar, color.teal, size = size.tiny)plotshape(showNativeFills and scored and barstate.isconfirmed and (f_lt(account.holdings.get(0).executedDelta, 0)), "Native sell fill", shape.triangledown, location.abovebar, color.red, size = size.tiny)plot(a.history.get(0).lag(), "a_feature_001", display = display.data_window)plot(a.history.get(1).lag(), "a_feature_002", display = display.data_window)plot(a.history.get(2).lag(), "a_feature_003", display = display.data_window)plot(a.history.get(3).lag(), "a_feature_004", display = display.data_window)plot(a.history.get(4).lag(), "a_feature_005", display = display.data_window)plot(a.history.get(5).lag(), "a_feature_006", display = display.data_window)plot(a.history.get(6).lag(), "a_feature_007", display = display.data_window)plot(a.history.get(7).lag(), "a_feature_008", display = display.data_window)plot(a.history.get(8).lag(), "a_feature_009", display = display.data_window)plot(a.history.get(9).lag(), "a_feature_010", display = display.data_window)plot(a.history.get(10).lag(), "a_feature_011", display = display.data_window)plot(a.history.get(11).lag(), "a_feature_012", display = display.data_window)plot(a.history.get(12).lag(), "a_feature_013", display = display.data_window)plot(a.history.get(13).lag(), "a_feature_014", display = display.data_window)plot(a.history.get(14).lag(), "a_feature_015", display = display.data_window)plot(a.history.get(15).lag(), "a_feature_016", display = display.data_window)plot(a.history.get(16).lag(), "a_feature_017", display = display.data_window)plot(a.history.get(17).lag(), "a_feature_018", display = display.data_window)plot(a.history.get(18).lag(), "a_feature_019", display = display.data_window)plot(a.history.get(19).lag(), "a_feature_020", display = display.data_window)plot(a.history.get(20).lag(), "a_feature_021", display = display.data_window)plot(a.history.get(21).lag(), "a_feature_022", display = display.data_window)plot(a.history.get(22).lag(), "a_feature_023", display = display.data_window)plot(a.history.get(23).lag(), "a_feature_024", display = display.data_window)plot(a.calculation.openingRangeHigh, "openingRangeHigh")plot(a.calculation.openingRangeMid, "openingRangeMid")plot(a.calculation.openingRangeLow, "openingRangeLow")