diff --git a/common/cache/README.md b/common/cache/README.md
new file mode 100644
index 00000000..8703f2f9
--- /dev/null
+++ b/common/cache/README.md
@@ -0,0 +1,67 @@
+# GoCryptoTrader package cache
+
+
+
+
+[](https://travis-ci.org/thrasher-corp/gocryptotrader)
+[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
+[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/portfolio)
+[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
+[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
+
+
+This cache package is part of the GoCryptoTrader codebase.
+
+## This is still in active development
+
+You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
+
+Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
+
+## Current Features for cache package
+
++ Basic LRU cache system with both goroutine safe (via mutex locking) and non-goroutine safe options
+
+## How to use
+
+##### Basic Usage:
+
+```go
+package main
+
+import ("github.com/thrasher-corp/gocryptotrader/common/cache")
+
+func main() {
+ lruCache := cache.New(5)
+ lruCache.Add("hello", "world")
+ c := lruCache.Contains("hello")
+ if !c {
+ fmt.Println("expected cache to contain \"hello\" key")
+ }
+
+ v := lruCache.Get("hello")
+ if v == nil {
+ fmt.Println("expected cache to contain \"hello\" key")
+ }
+ fmt.Println(v)
+}
+```
+## Contribution
+
+Please feel free to submit any pull requests or suggest any desired features to be added.
+
+When submitting a PR, please abide by our coding guidelines:
+
++ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
++ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
++ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
++ Pull requests need to be based on and opened against the `master` branch.
+
+## Donations
+
+
+
+If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
+
+***1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB***
+
diff --git a/common/cache/cache.go b/common/cache/cache.go
new file mode 100644
index 00000000..038141aa
--- /dev/null
+++ b/common/cache/cache.go
@@ -0,0 +1,75 @@
+package cache
+
+// New returns a new concurrent-safe LRU cache with input capacity
+func New(capacity uint64) *LRUCache {
+ return &LRUCache{
+ lru: NewLRUCache(capacity),
+ }
+}
+
+// Add new entry to Cache return true if entry removed
+func (l *LRUCache) Add(k, v interface{}) {
+ l.m.Lock()
+ l.lru.Add(k, v)
+ l.m.Unlock()
+}
+
+// Get looks up a key's value from the cache.
+func (l *LRUCache) Get(key interface{}) (value interface{}) {
+ l.m.Lock()
+ defer l.m.Unlock()
+ return l.lru.Get(key)
+}
+
+// GetOldest looks up old key's value from the cache.
+func (l *LRUCache) getOldest() (key, value interface{}) {
+ l.m.Lock()
+ defer l.m.Unlock()
+ return l.lru.getOldest()
+}
+
+// getNewest looks up a key's value from the cache.
+func (l *LRUCache) getNewest() (key, value interface{}) {
+ l.m.Lock()
+ defer l.m.Unlock()
+ return l.lru.getNewest()
+}
+
+// ContainsOrAdd checks if cache contains key if not adds to cache
+func (l *LRUCache) ContainsOrAdd(key, value interface{}) bool {
+ l.m.Lock()
+ defer l.m.Unlock()
+ if l.lru.Contains(key) {
+ return true
+ }
+ l.lru.Add(key, value)
+ return false
+}
+
+// Contains checks if cache contains key
+func (l *LRUCache) Contains(key interface{}) bool {
+ l.m.Lock()
+ defer l.m.Unlock()
+ return l.lru.Contains(key)
+}
+
+// Remove entry from cache
+func (l *LRUCache) Remove(key interface{}) bool {
+ l.m.Lock()
+ defer l.m.Unlock()
+ return l.lru.Remove(key)
+}
+
+// Clear is used to clear the cache.
+func (l *LRUCache) Clear() {
+ l.m.Lock()
+ l.lru.Clear()
+ l.m.Unlock()
+}
+
+// Len returns the number of items in the cache.
+func (l *LRUCache) Len() uint64 {
+ l.m.Lock()
+ defer l.m.Unlock()
+ return l.lru.Len()
+}
diff --git a/common/cache/cache_test.go b/common/cache/cache_test.go
new file mode 100644
index 00000000..6ac4cd9a
--- /dev/null
+++ b/common/cache/cache_test.go
@@ -0,0 +1,145 @@
+package cache
+
+import (
+ "testing"
+)
+
+func TestCache(t *testing.T) {
+ lruCache := New(5)
+ lruCache.Add("hello", "world")
+ c := lruCache.Contains("hello")
+ if !c {
+ t.Fatal("expected cache to contain \"hello\" key")
+ }
+
+ v := lruCache.Get("hello")
+ if v == nil {
+ t.Fatal("expected cache to contain \"hello\" key")
+ }
+ if v.(string) != "world" {
+ t.Fatal("expected \"hello\" key to contain value \"world\"")
+ }
+
+ r := lruCache.Remove("hello")
+ if !r {
+ t.Fatal("expected \"hello\" key to be removed from cache")
+ }
+
+ v = lruCache.Get("hello")
+ if v != nil {
+ t.Fatal("expected cache to not contain \"hello\" key")
+ }
+}
+
+func TestContainsOrAdd(t *testing.T) {
+ lruCache := New(5)
+
+ if lruCache.ContainsOrAdd("hello", "world") {
+ t.Fatal("expected ContainsOrAdd() to add new key when not found")
+ }
+
+ if !lruCache.ContainsOrAdd("hello", "world") {
+ t.Fatal("expected ContainsOrAdd() to return true when key found")
+ }
+}
+
+func TestClear(t *testing.T) {
+ lruCache := New(5)
+ for x := 0; x < 5; x++ {
+ lruCache.Add(x, x)
+ }
+ if lruCache.Len() != 5 {
+ t.Fatal("expected cache to have 5 entries")
+ }
+ lruCache.Clear()
+ if lruCache.Len() != 0 {
+ t.Fatal("expected cache to have 0 entries")
+ }
+}
+
+func TestAdd(t *testing.T) {
+ lruCache := New(2)
+ lruCache.Add(1, 1)
+ lruCache.Add(2, 2)
+ if lruCache.Len() != 2 {
+ t.Fatal("expected cache to have 2 entries")
+ }
+ lruCache.Add(3, 3)
+ if lruCache.Len() != 2 {
+ t.Fatal("expected cache to have 2 entries")
+ }
+
+ v := lruCache.Get(1)
+ if v != nil {
+ t.Fatal("expected cache to no longer contain \"1\" key")
+ }
+ v = lruCache.Get(2)
+ if v == nil {
+ t.Fatal("expected cache to contain \"2\" key")
+ }
+ if v.(int) != 2 {
+ t.Fatal("expected \"2\" key to contain value \"2\"")
+ }
+ k, v := lruCache.getNewest()
+ if k.(int) != 2 {
+ t.Fatal("expected latest key to be 2")
+ }
+ if v.(int) != 2 {
+ t.Fatal("expected latest value to be 2")
+ }
+ lruCache.Add(3, 3)
+ k, _ = lruCache.getNewest()
+ if k.(int) != 3 {
+ t.Fatal("expected latest key to be 3")
+ }
+ k, _ = lruCache.getOldest()
+ if k.(int) != 2 {
+ t.Fatal("expected oldest key to be 2")
+ }
+ k, v = lruCache.getOldest()
+ if k.(int) != 2 {
+ t.Fatal("expected oldest key to be 2")
+ }
+ if v.(int) != 2 {
+ t.Fatal("expected latest value to be 2")
+ }
+ lruCache.Add(2, 2)
+ k, _ = lruCache.getNewest()
+ if k.(int) != 2 {
+ t.Fatal("expected latest key to be 2")
+ }
+ k, _ = lruCache.getOldest()
+ if k.(int) != 3 {
+ t.Fatal("expected oldest key to be 3")
+ }
+}
+
+func TestRemove(t *testing.T) {
+ lruCache := New(2)
+ lruCache.Add(1, 1)
+
+ v := lruCache.Remove(1)
+ if !v {
+ t.Fatal("expected remove on valid key to return true")
+ }
+ v = lruCache.Remove(2)
+ if v {
+ t.Fatal("expected remove on invalid key to return false")
+ }
+}
+
+func TestGetNewest(t *testing.T) {
+ lruCache := New(2)
+ k, _ := lruCache.getNewest()
+ if k != nil {
+ t.Fatal("expected GetNewest() on empty cache to return nil")
+ }
+}
+
+func TestGetOldest(t *testing.T) {
+ lruCache := New(2)
+ k, _ := lruCache.getOldest()
+ if k != nil {
+ t.Fatal("expected GetOldest() on empty cache to return nil")
+ }
+}
diff --git a/common/cache/cache_types.go b/common/cache/cache_types.go
new file mode 100644
index 00000000..64605340
--- /dev/null
+++ b/common/cache/cache_types.go
@@ -0,0 +1,25 @@
+package cache
+
+import (
+ "container/list"
+ "sync"
+)
+
+// LRUCache thread safe fixed size LRU cache
+type LRUCache struct {
+ lru *LRU
+ m sync.Mutex
+}
+
+// LRU non-thread safe fixed size LRU cache
+type LRU struct {
+ Cap uint64
+ l *list.List
+ items map[interface{}]*list.Element
+}
+
+// item holds key/value for the cache
+type item struct {
+ key interface{}
+ value interface{}
+}
diff --git a/common/cache/lru.go b/common/cache/lru.go
new file mode 100644
index 00000000..995d05b4
--- /dev/null
+++ b/common/cache/lru.go
@@ -0,0 +1,106 @@
+/*
+ LRU Cache package
+
+ Based off information obtained from:
+
+ https://girai.dev/blog/lru-cache-implementation-in-go/
+ https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)
+*/
+
+package cache
+
+import "container/list"
+
+// NewLRUCache returns a new non-concurrent-safe LRU cache with input capacity
+func NewLRUCache(capacity uint64) *LRU {
+ return &LRU{
+ Cap: capacity,
+ l: list.New(),
+ items: make(map[interface{}]*list.Element),
+ }
+}
+
+// Add adds a value to the cache
+func (l *LRU) Add(key, value interface{}) {
+ if f, o := l.items[key]; o {
+ l.l.MoveToFront(f)
+ f.Value.(*item).value = value
+ return
+ }
+
+ newItem := &item{key, value}
+ itemList := l.l.PushFront(newItem)
+ l.items[key] = itemList
+ if l.Len() > l.Cap {
+ l.removeOldestEntry()
+ }
+}
+
+// Get returns keys value from cache if found
+func (l *LRU) Get(key interface{}) interface{} {
+ if i, f := l.items[key]; f {
+ l.l.MoveToFront(i)
+ return i.Value.(*item).value
+ }
+ return nil
+}
+
+// GetOldest returns the oldest entry
+func (l *LRU) getOldest() (key, value interface{}) {
+ x := l.l.Back()
+ if x != nil {
+ return x.Value.(*item).key, x.Value.(*item).value
+ }
+ return
+}
+
+// GetNewest returns the newest entry
+func (l *LRU) getNewest() (key, value interface{}) {
+ x := l.l.Front()
+ if x != nil {
+ return x.Value.(*item).key, x.Value.(*item).value
+ }
+ return
+}
+
+// Contains check if key is in cache this does not update LRU
+func (l *LRU) Contains(key interface{}) (f bool) {
+ _, f = l.items[key]
+ return
+}
+
+// Remove removes key from the cache, if the key was removed.
+func (l *LRU) Remove(key interface{}) bool {
+ if i, f := l.items[key]; f {
+ l.removeElement(i)
+ return true
+ }
+ return false
+}
+
+// Clear is used to completely clear the cache.
+func (l *LRU) Clear() {
+ for x := range l.items {
+ delete(l.items, l.items[x])
+ }
+ l.l.Init()
+}
+
+// Len returns length of l
+func (l *LRU) Len() uint64 {
+ return uint64(l.l.Len())
+}
+
+// removeOldest removes the oldest item from the cache.
+func (l *LRU) removeOldestEntry() {
+ i := l.l.Back()
+ if i != nil {
+ l.removeElement(i)
+ }
+}
+
+// removeElement element from the cache
+func (l *LRU) removeElement(e *list.Element) {
+ l.l.Remove(e)
+ delete(l.items, e.Value.(*item).key)
+}