Skip to content

Instantly share code, notes, and snippets.

@seglo
Last active July 27, 2017 17:55
Show Gist options
  • Save seglo/28e9883a451c39bbf14f913e6cdf410a to your computer and use it in GitHub Desktop.
Save seglo/28e9883a451c39bbf14f913e6cdf410a to your computer and use it in GitHub Desktop.
A programming assignment I did for an onsite interview to do several kinds of analytics on stock trade data using core Scala lib only
import scala.io.Source
import scala.util._
import scala.math.BigDecimal.RoundingMode
import java.io.File
import scala.collection.immutable.HashMap
object AssetRiskApp extends App {
val dataDir = """C:\Users\MyUser\Desktop\data"""
val marksDataFile = """C:\Users\MyUser\Desktop\data\marks.txt"""
// Parse file for current ticker prices.
// Input per line: "ABC 123.45"
val tickerPrices = for {
line <- Source.fromFile(new File(marksDataFile)).getLines()
fields = line.split("""\s""") if fields.length == 2
} yield TickerPrice(fields)
val tickerPriceMap = HashMap(tickerPrices.map(t => (t.ticker, t)).toSeq: _*)
//println(tickerPriceMap)
val tradeData: Seq[Trade] = getTradeData(mock = false)
// 1. Our Asset Risk
assetRisk()
// 2. Our Biggest Dollar Trading Volume Counter Parties
biggestTradingVolumeParties()
def assetRisk() = {
val tradeTotals = tradeData
.foldLeft(HashMap[(String, String), AssetTotal]()) { case (acc, trade) =>
val at = acc.getOrElse((trade.tickerSymbol, trade.counterPartyName), AssetTotal(trade.tickerSymbol, trade.counterPartyName, 0))
val newAt = at.copy(
shares = at.shares + trade.realNumberOfShares
)
acc + (((newAt.ticker, newAt.counterPartyName), newAt))
}
.values
.toSeq
println("Top 20 Long Assets by Counter Party")
tradeTotals
.sortBy(-_.shares)
.take(20)
.foreach { total =>
val currentValue = tickerPriceMap(total.ticker).price * total.shares
println(s"Counter Party Name: ${total.counterPartyName}, Asset: ${total.ticker}, Number of Shares: ${total.shares}, Current Market Price: $currentValue")
}
}
def biggestTradingVolumeParties() = {
val partyTotals = tradeData
.foldLeft(HashMap[String, PartyTradeVolumeTotal]()) { case (acc, trade) =>
val ptvt = acc.getOrElse(trade.counterPartyName, PartyTradeVolumeTotal(trade.counterPartyName, 0))
val newPtvt = ptvt.copy(
price = ptvt.price + trade.totalPrice
)
acc + ((newPtvt.counterPartyName, newPtvt))
}
.values
.toSeq
println("Top 20 Counter Parties Volume")
partyTotals
.sortBy(-_.price)
.take(20)
.foreach { total =>
println(s"Counter Party Name: ${total.counterPartyName}, Current Volume: ${total.price}")
}
}
// val topTwentyAssets.sortBy(-_.price).take(20)
//
// topTwentyAssets.foreach(println)
// Input files were tab delimited fields with the fields described in `Trade`
def getTradeData(mock: Boolean): Seq[Trade] =
if (mock)
Seq(
Trade("", "ABC", "AAPL", "BUY", 10, Option(BigDecimal(2.00))),
Trade("", "ABC", "AAPL", "SELL", 5, Option(BigDecimal(4.00))),
Trade("", "XYZ", "GOOG", "BUY", 10, Option(BigDecimal(3.00)))
)
else
for {
file <- getTradeFiles(dataDir)
line <- Source.fromFile(file).getLines()
fields = line.split("""\t""") if fields.length == 6
} yield Trade(fields)
def getTradeFiles(dir: String):List[File] = {
val d = new File(dir)
if (d.exists && d.isDirectory) {
d.listFiles.filter { f =>
f.isFile && f.getName() != "marks.txt"
}.toList
} else Nil
}
}
case class PartyTradeVolumeTotal(counterPartyName: String, price: BigDecimal)
case class AssetTotal(ticker: String, counterPartyName: String, shares: Int)
case class TickerPrice(ticker: String, price: BigDecimal)
object TickerPrice {
def apply(fields: Seq[String]):TickerPrice =
TickerPrice(fields(0), Utils.parsePrice(fields(1)).getOrElse(0))
}
case class Trade(
date: String,
counterPartyName: String,
tickerSymbol: String,
buySell: String,
numberOfShares: Int,
tradePrice: Option[BigDecimal]) {
val realNumberOfShares = if (buySell == "BUY") numberOfShares else -numberOfShares
val realPrice: BigDecimal = tradePrice.getOrElse(0)
val realBuySellPrice: BigDecimal = if (buySell == "BUY") realPrice else -realPrice
val totalPrice: BigDecimal = realBuySellPrice * numberOfShares
}
object Trade {
def apply(fields: Seq[String]):Trade = {
Trade(fields(0), fields(1), fields(2), fields(3), fields(4).toInt, Utils.parsePrice(fields(5)))
}
}
object Utils {
def parsePrice(price: String): Option[BigDecimal] =
Try {
val dec = BigDecimal(price)
dec.setScale(2, RoundingMode.HALF_EVEN)
Some(dec)
}.getOrElse(None)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment