Creating an option model

Now we need to create an OptionModel to act as the container and the factory for the properties of the option. It takes the following parameters and creates a list of option properties, propsList, by accessing the data source of the four features described earlier:

  • The symbol of the security.
  • The strike price for option, strikePrice.
  • The source of the data, src.
  • The minimum time decay or time to expiration, minTDecay. Out-of-the-money options expire worthlessly, and in-the-money options have a very different price behavior as they get closer to the expiration. Therefore, the last minTDecay trading sessions prior to the expiration date are not used in the training process.
  • The number of steps (or buckets), nSteps, is used in approximating the values of each feature. For instance, an approximation of four steps creates four buckets: (0, 25), (25, 50), (50, 75), and (75, 100).

Then it assembles OptionProperties and computes the normalized minimum time to the expiration of the option. Then it computes an approximation of the value of options by discretization of the actual value in multiple levels from an array of options prices; finally it returns a map of an array of levels for the option price and accuracy. Here is the constructor of the class:

class OptionModel(
symbol: String,
strikePrice: Double,
src: DataSource,
minExpT: Int,
nSteps: Int
)

Inside this class implementation, at first, a validation is done using the check() method, by checking the following:

  • strikePrice: A positive price is required
  • minExpT: This has to be between 2 and 16
  • nSteps: Requires a minimum of two steps

Here's the invocation of this method:

check(strikePrice, minExpT, nSteps)

The signature of the preceding method is shown in the following code:

def check(strikePrice: Double, minExpT: Int, nSteps: Int): Unit = {
require(strikePrice > 0.0, s"OptionModel.check price found $strikePrice required > 0")
require(minExpT > 2 && minExpT < 16,s"OptionModel.check Minimum expiration time found $minExpT required ]2, 16[")
require(nSteps > 1,s"OptionModel.check, number of steps found $nSteps required > 1")
}

Once the preceding constraint is satisfied, the list of option properties, named propsList, is created as follows:

val propsList = (for {
price <- src.get(adjClose)
volatility <- src.get(volatility)
nVolatility <- normalize[Double](volatility)
vltyByVol <- src.get(volatilityByVol)
nVltyByVol <- normalize[Double](vltyByVol)
priceToStrike <- normalize[Double](price.map(p => 1.0 - strikePrice / p))
}
yield {
nVolatility.zipWithIndex./:(List[OptionProperty]()) {
case (xs, (v, n)) =>
val normDecay = (n + minExpT).toDouble / (price.size + minExpT)
new OptionProperty(normDecay, v, nVltyByVol(n), priceToStrike(n)) :: xs
}
.drop(2).reverse
}).get

 In the preceding code block, the factory uses the zipWithIndex Scala method to represent the index of the trading sessions. All feature values are normalized over the interval (0, 1), including the time decay (or time to expiration) of the normDecay option.

The quantize() method of the OptionModel class converts the normalized value of each option property of features into an array of bucket indices. It returns a map of profit and loss for each bucket keyed on the array of bucket indices:

def quantize(o: Array[Double]): Map[Array[Int], Double] = {
val mapper = new mutable.HashMap[Int, Array[Int]]
val acc: NumericAccumulator[Int] = propsList.view.map(_.toArray)
map(toArrayInt(_)).map(ar => {
val enc = encode(ar)
mapper.put(enc, ar)
enc
})
.zip(o)./:(
new NumericAccumulator[Int]) {
case (_acc, (t, y)) => _acc += (t, y); _acc
}
acc.map {
case (k, (v, w)) => (k, v / w) }
.map {
case (k, v) => (mapper(k), v) }.toMap
}

The method also creates a mapper instance to index the array of buckets. An accumulator, acc, of type NumericAccumulator extends the Map[Int, (Int, Double)] and computes this tuple (number of occurrences of features on each bucket, sum of the increase or decrease of the option price).

The toArrayInt method converts the value of each option property (timeToExp, volatility, and so on) into the index of the appropriate bucket. The array of indices is then encoded to generate the id or index of a state. The method updates the accumulator with the number of occurrences and the total profit and loss for a trading session for the option. It finally computes the reward on each action by averaging the profit and loss on each bucket. The signature of the encode(), toArrayInt() is given in the following code:

private def encode(arr: Array[Int]): Int = arr./:((1, 0)) { 
case ((s, t), n) => (s * nSteps, t + s * n) }._2
private def toArrayInt(feature: Array[Double]): Array[Int] = feature.map(x => (nSteps *
x).floor.toInt)

final class NumericAccumulator[T]
extends mutable.HashMap[T, (Int, Double)] {
def +=(key: T, x: Double): Option[(Int, Double)] = {
val newValue =
if (contains(key)) (get(key).get._1 + 1, get(key).get._2 + x)
else (1, x)
super.put(key, newValue)
}
}

Finally, and most importantly, if the preceding constraints are satisfied (you can modify these constraints though) and once the instantiation of the OptionModel class generates a list of OptionProperty elements if the constructor succeeds; otherwise, it generates an empty list.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset