Skip to content

Instantly share code, notes, and snippets.

@Synesso
Last active April 8, 2018 21:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Synesso/1d25c953a7090e0ae1bb67a2acdded63 to your computer and use it in GitHub Desktop.
Save Synesso/1d25c953a7090e0ae1bb67a2acdded63 to your computer and use it in GitHub Desktop.
Analysis of gap trades using MT4 data extracts
import java.time._
import scala.io._
import scala.util._
case class Candle(dateTime: LocalDateTime, open: Double, high: Double, low: Double, close: Double)
val significant = 0.003
val formatter = java.time.format.DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm")
def parseCandle(s: String): Try[Candle] = Try {
val Array(day, hour, open, high, low, close, _) = s.split(",")
Candle(
LocalDateTime.parse(s"$day $hour", formatter),
open.toDouble,
high.toDouble,
low.toDouble,
close.toDouble
)
}
val candles = Source.fromFile("GBPJPY4HR.csv").getLines.flatMap(parseCandle(_).toOption).toSeq
def epochSecond(dt: LocalDateTime) = dt.toEpochSecond(ZoneOffset.UTC)
def closesGap(last: Candle, first: Candle, current: Candle): Boolean = {
(last.close > first.open && current.high >= last.close) ||
(last.close <= first.open && current.low <= last.close)
}
def closedBetween(l: Candle, r: Candle): Boolean = r.dateTime.compareTo(l.dateTime) > 1
def hoursSince(first: Candle, current: Candle): Long = (epochSecond(current.dateTime) - epochSecond(first.dateTime)) / 3600
val z = Seq.empty[(Candle, Candle, Option[Candle])]
val gapCloses = candles.sliding(2).foldLeft(z) {
// l & r gap: add (l, r, None)
case (xs, Seq(l, r)) if closedBetween(l, r) => (l, r, None) +: xs
// acc is empty: do nothing
case (Nil, _) => Nil
// head is empty; r is too long after first: do nothing
case ((last, first, None) +: t, Seq(_, r)) if hoursSince(first, r) >= 24 => (last, first, None) +: t
// head is empty; r closes gap: modify to (last, first, Some(r))
case ((last, first, None) +: t, Seq(_, r)) if closesGap(last, first, r) => (last, first, Some(r)) +: t
// otherwise: do nothing
case (xs, _) => xs
}.reverse
def gap(last: Candle, first: Candle): Double = {
BigDecimal(math.abs(last.close - first.open)).setScale(6, BigDecimal.RoundingMode.HALF_UP).toDouble
}
def direction(last: Candle, first: Candle): String = {
val diff = gap(last, first)
if (last.close < first.open) f"up $diff%02f"
else f"down $diff%02f"
}
gapCloses.foreach {
case (last, first, None) =>
println(s"${first.dateTime} gapped ${direction(last, first)} and did not close the gap within 24 hours")
case (last, first, Some(closed)) =>
println(s"${first.dateTime} gapped ${direction(last, first)} and closed the gap within ${hoursSince(first, closed)} hours")
}
// percentage of more than 5pts gap
val (bigGaps, smallGaps) = gapCloses.partition{ case (last, first, _) => math.abs(gap(last, first)) >= significant }
val (closed, notClosed) = bigGaps.partition(_._3.nonEmpty)
val all = gapCloses.size
val smalls = smallGaps.size
val bigs = bigGaps.size
val closedBigs = closed.size
val unclosedBigs = notClosed.size
val avgAllGaps = {
val gaps = gapCloses.map{ case (last, first, _) => math.abs(gap(last, first)) }
gaps.sum * 1.0 / gaps.length
}
val avgBigGaps = {
val gaps = bigGaps.map{ case (last, first, _) => math.abs(gap(last, first)) }
gaps.sum * 1.0 / gaps.length
}
println(s"$smalls gaps < $significant ${smalls * 1.0 / all * 100.0}%")
println(s"$bigs gaps >= $significant ${bigs * 1.0 / all * 100.0}%")
println(s"$closedBigs of the big gaps are closed ${closedBigs * 1.0 / all * 100.0}% of all, ${closedBigs * 1.0 / bigs * 100.0}% of bigs")
println(s"$unclosedBigs of the big gaps are not closed ${unclosedBigs * 1.0 / all * 100.0}% of all, ${unclosedBigs * 1.0 / bigs * 100.0}% of bigs")
println(f"Average gap size of all gaps = $avgAllGaps%02f")
println(f"Average gap size of big gaps = $avgBigGaps%02f")
@Synesso
Copy link
Author

Synesso commented Apr 8, 2018

AUDUSD4HR

15 gaps < 0.0003 23.809523809523807%
48 gaps >= 0.0003 76.19047619047619%
40 of the big gaps are closed  63.49206349206349% of all, 83.33333333333334% of bigs
8 of the big gaps are not closed  12.698412698412698% of all, 16.666666666666664% of bigs

@Synesso
Copy link
Author

Synesso commented Apr 8, 2018

AUS2004HR

23 gaps < 5.0 35.38461538461539%
42 gaps >= 5.0 64.61538461538461%
26 of the big gaps are closed  40.0%
16 of the big gaps are not closed  24.615384615384617%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment