Last active
July 27, 2017 17:55
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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