diff --git a/currency/symbol/symbol.go b/currency/symbol/symbol.go index 14018b93..f9c97b89 100644 --- a/currency/symbol/symbol.go +++ b/currency/symbol/symbol.go @@ -2,6 +2,1516 @@ package symbol import "errors" +// Const declarations for individual currencies/tokens/fiat +// An ever growing list. Cares not for equivalence, just is +const ( + BTC = "BTC" + LTC = "LTC" + ETH = "ETH" + XRP = "XRP" + BCH = "BCH" + EOS = "EOS" + XLM = "XLM" + USDT = "USDT" + ADA = "ADA" + XMR = "XMR" + TRX = "TRX" + MIOTA = "MIOTA" + DASH = "DASH" + BNB = "BNB" + NEO = "NEO" + ETC = "ETC" + XEM = "XEM" + XTZ = "XTZ" + VET = "VET" + DOGE = "DOGE" + ZEC = "ZEC" + OMG = "OMG" + BTG = "BTG" + MKR = "MKR" + BCN = "BCN" + ONT = "ONT" + ZRX = "ZRX" + LSK = "LSK" + DCR = "DCR" + QTUM = "QTUM" + BCD = "BCD" + BTS = "BTS" + NANO = "NANO" + ZIL = "ZIL" + SC = "SC" + DGB = "DGB" + ICX = "ICX" + STEEM = "STEEM" + AE = "AE" + XVG = "XVG" + WAVES = "WAVES" + NPXS = "NPXS" + ETN = "ETN" + BTM = "BTM" + BAT = "BAT" + ETP = "ETP" + HOT = "HOT" + STRAT = "STRAT" + GNT = "GNT" + REP = "REP" + SNT = "SNT" + PPT = "PPT" + KMD = "KMD" + TUSD = "TUSD" + CNX = "CNX" + LINK = "LINK" + WTC = "WTC" + ARDR = "ARDR" + WAN = "WAN" + MITH = "MITH" + RDD = "RDD" + IOST = "IOST" + IOT = "IOT" + KCS = "KCS" + MAID = "MAID" + XET = "XET" + MOAC = "MOAC" + HC = "HC" + AION = "AION" + AOA = "AOA" + HT = "HT" + ELF = "ELF" + LRC = "LRC" + BNT = "BNT" + CMT = "CMT" + DGD = "DGD" + DCN = "DCN" + FUN = "FUN" + GXS = "GXS" + DROP = "DROP" + MANA = "MANA" + PAY = "PAY" + MCO = "MCO" + THETA = "THETA" + NXT = "NXT" + NOAH = "NOAH" + LOOM = "LOOM" + POWR = "POWR" + WAX = "WAX" + ELA = "ELA" + PIVX = "PIVX" + XIN = "XIN" + DAI = "DAI" + BTCP = "BTCP" + NEXO = "NEXO" + XBT = "XBT" + SAN = "SAN" + GAS = "GAS" + BCC = "BCC" + HCC = "HCC" + OAX = "OAX" + DNT = "DNT" + ICN = "ICN" + LLT = "LLT" + YOYO = "YOYO" + SNGLS = "SNGLS" + BQX = "BQX" + KNC = "KNC" + SNM = "SNM" + CTR = "CTR" + SALT = "SALT" + MDA = "MDA" + IOTA = "IOTA" + SUB = "SUB" + MTL = "MTL" + MTH = "MTH" + ENG = "ENG" + AST = "AST" + CLN = "CLN" + EDG = "EDG" + FIRST = "1ST" + GOLOS = "GOLOS" + ANT = "ANT" + GBG = "GBG" + HMQ = "HMQ" + INCNT = "INCNT" + ACE = "ACE" + ACT = "ACT" + AAC = "AAC" + AIDOC = "AIDOC" + SOC = "SOC" + ATL = "ATL" + AVT = "AVT" + BKX = "BKX" + BEC = "BEC" + VEE = "VEE" + PTOY = "PTOY" + CAG = "CAG" + CIC = "CIC" + CBT = "CBT" + CAN = "CAN" + DAT = "DAT" + DNA = "DNA" + INT = "INT" + IPC = "IPC" + ILA = "ILA" + LIGHT = "LIGHT" + MAG = "MAG" + AMM = "AMM" + MOF = "MOF" + MGC = "MGC" + OF = "OF" + LA = "LA" + LEV = "LEV" + NGC = "NGC" + OKB = "OKB" + MOT = "MOT" + PRA = "PRA" + R = "R" + SSC = "SSC" + SHOW = "SHOW" + SPF = "SPF" + SNC = "SNC" + SWFTC = "SWFTC" + TRA = "TRA" + TOPC = "TOPC" + TRIO = "TRIO" + QVT = "QVT" + UCT = "UCT" + UKG = "UKG" + UTK = "UTK" + VIU = "VIU" + WFEE = "WFEE" + WRC = "WRC" + UGC = "UGC" + YEE = "YEE" + YOYOW = "YOYOW" + ZIP = "ZIP" + READ = "READ" + RCT = "RCT" + REF = "REF" + XUC = "XUC" + FAIR = "FAIR" + GSC = "GSC" + HMC = "HMC" + PLU = "PLU" + PRO = "PRO" + QRL = "QRL" + REN = "REN" + ROUND = "ROUND" + SRN = "SRN" + XID = "XID" + SBD = "SBD" + TAAS = "TAAS" + TKN = "TKN" + VEN = "VEN" + VSL = "VSL" + TRST = "TRST" + XXX = "XXX" + IND = "IND" + LDC = "LDC" + GUP = "GUP" + MGO = "MGO" + MYST = "MYST" + NEU = "NEU" + NET = "NET" + BMC = "BMC" + BCAP = "BCAP" + TIME = "TIME" + CFI = "CFI" + EVX = "EVX" + REQ = "REQ" + VIB = "VIB" + ARK = "ARK" + MOD = "MOD" + ENJ = "ENJ" + STORJ = "STORJ" + RCN = "RCN" + NULS = "NULS" + RDN = "RDN" + DLT = "DLT" + AMB = "AMB" + BCPT = "BCPT" + ARN = "ARN" + GVT = "GVT" + CDT = "CDT" + POE = "POE" + QSP = "QSP" + XZC = "XZC" + TNT = "TNT" + FUEL = "FUEL" + ADX = "ADX" + CND = "CND" + LEND = "LEND" + WABI = "WABI" + SBTC = "SBTC" + BCX = "BCX" + TNB = "TNB" + GTO = "GTO" + OST = "OST" + CVC = "CVC" + DATA = "DATA" + ETF = "ETF" + BRD = "BRD" + NEBL = "NEBL" + VIBE = "VIBE" + LUN = "LUN" + CHAT = "CHAT" + RLC = "RLC" + INS = "INS" + VIA = "VIA" + BLZ = "BLZ" + SYS = "SYS" + NCASH = "NCASH" + POA = "POA" + STORM = "STORM" + WPR = "WPR" + QLC = "QLC" + GRS = "GRS" + CLOAK = "CLOAK" + ZEN = "ZEN" + SKY = "SKY" + IOTX = "IOTX" + QKC = "QKC" + AGI = "AGI" + NXS = "NXS" + EON = "EON" + KEY = "KEY" + NAS = "NAS" + ADD = "ADD" + MEETONE = "MEETONE" + ATD = "ATD" + MFT = "MFT" + EOP = "EOP" + DENT = "DENT" + IQ = "IQ" + DOCK = "DOCK" + POLY = "POLY" + VTHO = "VTHO" + ONG = "ONG" + PHX = "PHX" + GO = "GO" + PAX = "PAX" + EDO = "EDO" + WINGS = "WINGS" + NAV = "NAV" + TRIG = "TRIG" + APPC = "APPC" + KRW = "KRW" + HSR = "HSR" + ETHOS = "ETHOS" + CTXC = "CTXC" + ITC = "ITC" + TRUE = "TRUE" + ABT = "ABT" + RNT = "RNT" + PLY = "PLY" + PST = "PST" + KICK = "KICK" + BTCZ = "BTCZ" + DXT = "DXT" + STQ = "STQ" + INK = "INK" + HBZ = "HBZ" + USDT_ETH = "USDT_ETH" + QTUM_ETH = "QTUM_ETH" + BTM_ETH = "BTM_ETH" + FIL = "FIL" + STX = "STX" + BOT = "BOT" + VERI = "VERI" + ZSC = "ZSC" + QBT = "QBT" + MED = "MED" + QASH = "QASH" + MDS = "MDS" + GOD = "GOD" + SMT = "SMT" + BTF = "BTF" + NAS_ETH = "NAS_ETH" + TSL = "TSL" + BIFI = "BIFI" + BNTY = "BNTY" + DRGN = "DRGN" + GTC = "GTC" + MDT = "MDT" + QUN = "QUN" + GNX = "GNX" + DDD = "DDD" + BTO = "BTO" + TIO = "TIO" + OCN = "OCN" + RUFF = "RUFF" + TNC = "TNC" + SNET = "SNET" + COFI = "COFI" + ZPT = "ZPT" + JNT = "JNT" + MTN = "MTN" + GEM = "GEM" + DADI = "DADI" + RFR = "RFR" + MOBI = "MOBI" + LEDU = "LEDU" + DBC = "DBC" + MKR_OLD = "MKR_OLD" + DPY = "DPY" + BCDN = "BCDN" + EOSDAC = "EOSDAC" + TIPS = "TIPS" + XMC = "XMC" + PPS = "PPS" + BOE = "BOE" + MEDX = "MEDX" + SMT_ETH = "SMT_ETH" + CS = "CS" + MAN = "MAN" + REM = "REM" + LYM = "LYM" + INSTAR = "INSTAR" + BFT = "BFT" + IHT = "IHT" + SENC = "SENC" + TOMO = "TOMO" + ELEC = "ELEC" + SHIP = "SHIP" + TFD = "TFD" + HAV = "HAV" + HUR = "HUR" + LST = "LST" + LINO = "LINO" + SWTH = "SWTH" + NKN = "NKN" + SOUL = "SOUL" + GALA_NEO = "GALA_NEO" + LRN = "LRN" + GSE = "GSE" + RATING = "RATING" + HSC = "HSC" + HIT = "HIT" + DX = "DX" + BXC = "BXC" + GARD = "GARD" + FTI = "FTI" + SOP = "SOP" + LEMO = "LEMO" + RED = "RED" + LBA = "LBA" + KAN = "KAN" + OPEN = "OPEN" + SKM = "SKM" + NBAI = "NBAI" + UPP = "UPP" + ATMI = "ATMI" + TMT = "TMT" + BBK = "BBK" + EDR = "EDR" + MET = "MET" + TCT = "TCT" + EXC = "EXC" + CNC = "CNC" + TIX = "TIX" + XTC = "XTC" + BU = "BU" + GNO = "GNO" + MLN = "MLN" + XBC = "XBC" + BTCD = "BTCD" + BURST = "BURST" + CLAM = "CLAM" + XCP = "XCP" + EMC2 = "EMC2" + EXP = "EXP" + FCT = "FCT" + GAME = "GAME" + GRC = "GRC" + HUC = "HUC" + LBC = "LBC" + NMC = "NMC" + NEOS = "NEOS" + OMNI = "OMNI" + PASC = "PASC" + PPC = "PPC" + DSH = "DSH" + GML = "GML" + GSY = "GSY" + POT = "POT" + XPM = "XPM" + AMP = "AMP" + VRC = "VRC" + VTC = "VTC" + ZERO07 = "007" + BIT16 = "BIT16" + TWO015 = "2015" + TWO56 = "256" + TWOBACCO = "2BACCO" + TWOGIVE = "2GIVE" + THIRTY2BIT = "32BIT" + THREE65 = "365" + FOUR04 = "404" + SEVEN00 = "700" + EIGHTBIT = "8BIT" + ACLR = "ACLR" + ACES = "ACES" + ACPR = "ACPR" + ACID = "ACID" + ACOIN = "ACOIN" + ACRN = "ACRN" + ADAM = "ADAM" + ADT = "ADT" + AIB = "AIB" + ADZ = "ADZ" + AECC = "AECC" + AM = "AM" + AGRI = "AGRI" + AGT = "AGT" + AIR = "AIR" + ALEX = "ALEX" + AUM = "AUM" + ALIEN = "ALIEN" + ALIS = "ALIS" + ALL = "ALL" + ASAFE = "ASAFE" + AMBER = "AMBER" + AMS = "AMS" + ANAL = "ANAL" + ACP = "ACP" + ANI = "ANI" + ANTI = "ANTI" + ALTC = "ALTC" + APT = "APT" + ARCO = "ARCO" + ALC = "ALC" + ARB = "ARB" + ARCT = "ARCT" + ARCX = "ARCX" + ARGUS = "ARGUS" + ARH = "ARH" + ARM = "ARM" + ARNA = "ARNA" + ARPA = "ARPA" + ARTA = "ARTA" + ABY = "ABY" + ARTC = "ARTC" + AL = "AL" + ASN = "ASN" + ADCN = "ADCN" + ATB = "ATB" + ATM = "ATM" + ATMCHA = "ATMCHA" + ATOM = "ATOM" + ADC = "ADC" + ARE = "ARE" + AUR = "AUR" + AV = "AV" + AXIOM = "AXIOM" + B2B = "B2B" + B2 = "B2" + B3 = "B3" + BAB = "BAB" + BAN = "BAN" + BamitCoin = "BamitCoin" + NANAS = "NANAS" + BBCC = "BBCC" + BTA = "BTA" + BSTK = "BSTK" + BATL = "BATL" + BBH = "BBH" + BITB = "BITB" + BRDD = "BRDD" + XBTS = "XBTS" + BVC = "BVC" + CHATX = "CHATX" + BEEP = "BEEP" + BEEZ = "BEEZ" + BENJI = "BENJI" + BERN = "BERN" + PROFIT = "PROFIT" + BEST = "BEST" + BGF = "BGF" + BIGUP = "BIGUP" + BLRY = "BLRY" + BILL = "BILL" + BIOB = "BIOB" + BIO = "BIO" + BIOS = "BIOS" + BPTN = "BPTN" + BTCA = "BTCA" + BA = "BA" + BAC = "BAC" + BBT = "BBT" + BOSS = "BOSS" + BRONZ = "BRONZ" + CAT = "CAT" + BTD = "BTD" + XBTC21 = "XBTC21" + BCA = "BCA" + BCP = "BCP" + BTDOLL = "BTDOLL" + LIZA = "LIZA" + BTCRED = "BTCRED" + BTCS = "BTCS" + BTU = "BTU" + BUM = "BUM" + LITE = "LITE" + BCM = "BCM" + BCS = "BCS" + BTCU = "BTCU" + BM = "BM" + BTCRY = "BTCRY" + BTCR = "BTCR" + HIRE = "HIRE" + STU = "STU" + BITOK = "BITOK" + BITON = "BITON" + BPC = "BPC" + BPOK = "BPOK" + BTP = "BTP" + BITCNY = "bitCNY" + RNTB = "RNTB" + BSH = "BSH" + XBS = "XBS" + BITS = "BITS" + BST = "BST" + BXT = "BXT" + VEG = "VEG" + VOLT = "VOLT" + BTV = "BTV" + BITZ = "BITZ" + BTZ = "BTZ" + BHC = "BHC" + BDC = "BDC" + JACK = "JACK" + BS = "BS" + BSTAR = "BSTAR" + BLAZR = "BLAZR" + BOD = "BOD" + BLUE = "BLUE" + BLU = "BLU" + BLUS = "BLUS" + BMT = "BMT" + BOLI = "BOLI" + BOMB = "BOMB" + BON = "BON" + BOOM = "BOOM" + BOSON = "BOSON" + BSC = "BSC" + BRH = "BRH" + BRAIN = "BRAIN" + BRE = "BRE" + BTCM = "BTCM" + BTCO = "BTCO" + TALK = "TALK" + BUB = "BUB" + BUY = "BUY" + BUZZ = "BUZZ" + BTH = "BTH" + C0C0 = "C0C0" + CAB = "CAB" + CF = "CF" + CLO = "CLO" + CAM = "CAM" + CD = "CD" + CANN = "CANN" + CNNC = "CNNC" + CPC = "CPC" + CST = "CST" + CAPT = "CAPT" + CARBON = "CARBON" + CME = "CME" + CTK = "CTK" + CBD = "CBD" + CCC = "CCC" + CNT = "CNT" + XCE = "XCE" + CHRG = "CHRG" + CHEMX = "CHEMX" + CHESS = "CHESS" + CKS = "CKS" + CHILL = "CHILL" + CHIP = "CHIP" + CHOOF = "CHOOF" + CRX = "CRX" + CIN = "CIN" + POLL = "POLL" + CLICK = "CLICK" + CLINT = "CLINT" + CLUB = "CLUB" + CLUD = "CLUD" + COX = "COX" + COXST = "COXST" + CFC = "CFC" + CTIC2 = "CTIC2" + COIN = "COIN" + BTTF = "BTTF" + C2 = "C2" + CAID = "CAID" + CL = "CL" + CTIC = "CTIC" + CXT = "CXT" + CHP = "CHP" + CV2 = "CV2" + COC = "COC" + COMP = "COMP" + CMS = "CMS" + CONX = "CONX" + CCX = "CCX" + CLR = "CLR" + CORAL = "CORAL" + CORG = "CORG" + CSMIC = "CSMIC" + CMC = "CMC" + COV = "COV" + COVX = "COVX" + CRAB = "CRAB" + CRAFT = "CRAFT" + CRNK = "CRNK" + CRAVE = "CRAVE" + CRM = "CRM" + XCRE = "XCRE" + CREDIT = "CREDIT" + CREVA = "CREVA" + CRIME = "CRIME" + CROC = "CROC" + CRC = "CRC" + CRW = "CRW" + CRY = "CRY" + CBX = "CBX" + TKTX = "TKTX" + CB = "CB" + CIRC = "CIRC" + CCB = "CCB" + CDO = "CDO" + CG = "CG" + CJ = "CJ" + CJC = "CJC" + CYT = "CYT" + CRPS = "CRPS" + PING = "PING" + CWXT = "CWXT" + CCT = "CCT" + CTL = "CTL" + CURVES = "CURVES" + CC = "CC" + CYC = "CYC" + CYG = "CYG" + CYP = "CYP" + FUNK = "FUNK" + CZECO = "CZECO" + DALC = "DALC" + DLISK = "DLISK" + MOOND = "MOOND" + DB = "DB" + DCC = "DCC" + DCYP = "DCYP" + DETH = "DETH" + DKC = "DKC" + DISK = "DISK" + DRKT = "DRKT" + DTT = "DTT" + DASHS = "DASHS" + DBTC = "DBTC" + DCT = "DCT" + DBET = "DBET" + DEC = "DEC" + DECR = "DECR" + DEA = "DEA" + DPAY = "DPAY" + DCRE = "DCRE" + DC = "DC" + DES = "DES" + DEM = "DEM" + DXC = "DXC" + DCK = "DCK" + CUBE = "CUBE" + DGMS = "DGMS" + DBG = "DBG" + DGCS = "DGCS" + DBLK = "DBLK" + DIME = "DIME" + DIRT = "DIRT" + DVD = "DVD" + DMT = "DMT" + NOTE = "NOTE" + DGORE = "DGORE" + DLC = "DLC" + DRT = "DRT" + DOTA = "DOTA" + DOX = "DOX" + DRA = "DRA" + DFT = "DFT" + XDB = "XDB" + DRM = "DRM" + DRZ = "DRZ" + DRACO = "DRACO" + DBIC = "DBIC" + DUB = "DUB" + GUM = "GUM" + DUR = "DUR" + DUST = "DUST" + DUX = "DUX" + DXO = "DXO" + ECN = "ECN" + EDR2 = "EDR2" + EA = "EA" + EAGS = "EAGS" + EMT = "EMT" + EBONUS = "EBONUS" + ECCHI = "ECCHI" + EKO = "EKO" + ECLI = "ECLI" + ECOB = "ECOB" + ECO = "ECO" + EDIT = "EDIT" + EDRC = "EDRC" + EDC = "EDC" + EGAME = "EGAME" + EGG = "EGG" + EGO = "EGO" + ELC = "ELC" + ELCO = "ELCO" + ECA = "ECA" + EPC = "EPC" + ELE = "ELE" + ONE337 = "1337" + EMB = "EMB" + EMC = "EMC" + EPY = "EPY" + EMPC = "EMPC" + EMP = "EMP" + ENE = "ENE" + EET = "EET" + XNG = "XNG" + EGMA = "EGMA" + ENTER = "ENTER" + ETRUST = "ETRUST" + EQL = "EQL" + EQM = "EQM" + EQT = "EQT" + ERR = "ERR" + ESC = "ESC" + ESP = "ESP" + ENT = "ENT" + ETCO = "ETCO" + DOGETH = "DOGETH" + ECASH = "ECASH" + ELITE = "ELITE" + ETHS = "ETHS" + ETL = "ETL" + ETZ = "ETZ" + EUC = "EUC" + EURC = "EURC" + EUROPE = "EUROPE" + EVA = "EVA" + EGC = "EGC" + EOC = "EOC" + EVIL = "EVIL" + EVO = "EVO" + EXB = "EXB" + EXIT = "EXIT" + XT = "XT" + F16 = "F16" + FADE = "FADE" + FAZZ = "FAZZ" + FX = "FX" + FIDEL = "FIDEL" + FIDGT = "FIDGT" + FIND = "FIND" + FPC = "FPC" + FIRE = "FIRE" + FFC = "FFC" + FRST = "FRST" + FIST = "FIST" + FIT = "FIT" + FLX = "FLX" + FLVR = "FLVR" + FLY = "FLY" + FONZ = "FONZ" + XFCX = "XFCX" + FOREX = "FOREX" + FRN = "FRN" + FRK = "FRK" + FRWC = "FRWC" + FGZ = "FGZ" + FRE = "FRE" + FRDC = "FRDC" + FJC = "FJC" + FURY = "FURY" + FSN = "FSN" + FCASH = "FCASH" + FTO = "FTO" + FUZZ = "FUZZ" + GAKH = "GAKH" + GBT = "GBT" + UNITS = "UNITS" + FOUR20G = "420G" + GENIUS = "GENIUS" + GEN = "GEN" + GEO = "GEO" + GER = "GER" + GSR = "GSR" + SPKTR = "SPKTR" + GIFT = "GIFT" + WTT = "WTT" + GHS = "GHS" + GIG = "GIG" + GOT = "GOT" + XGTC = "XGTC" + GIZ = "GIZ" + GLO = "GLO" + GCR = "GCR" + BSTY = "BSTY" + GLC = "GLC" + GSX = "GSX" + GOAT = "GOAT" + GB = "GB" + GFL = "GFL" + MNTP = "MNTP" + GP = "GP" + GLUCK = "GLUCK" + GOON = "GOON" + GTFO = "GTFO" + GOTX = "GOTX" + GPU = "GPU" + GRF = "GRF" + GRAM = "GRAM" + GRAV = "GRAV" + GBIT = "GBIT" + GREED = "GREED" + GE = "GE" + GREENF = "GREENF" + GRE = "GRE" + GREXIT = "GREXIT" + GMCX = "GMCX" + GROW = "GROW" + GSM = "GSM" + GT = "GT" + NLG = "NLG" + HKN = "HKN" + HAC = "HAC" + HALLO = "HALLO" + HAMS = "HAMS" + HPC = "HPC" + HAWK = "HAWK" + HAZE = "HAZE" + HZT = "HZT" + HDG = "HDG" + HEDG = "HEDG" + HEEL = "HEEL" + HMP = "HMP" + PLAY = "PLAY" + HXX = "HXX" + XHI = "XHI" + HVCO = "HVCO" + HTC = "HTC" + MINH = "MINH" + HODL = "HODL" + HON = "HON" + HOPE = "HOPE" + HQX = "HQX" + HSP = "HSP" + HTML5 = "HTML5" + HYPERX = "HYPERX" + HPS = "HPS" + IOC = "IOC" + IBANK = "IBANK" + IBITS = "IBITS" + ICASH = "ICASH" + ICOB = "ICOB" + ICON = "ICON" + IETH = "IETH" + ILM = "ILM" + IMPS = "IMPS" + NKA = "NKA" + INCP = "INCP" + IN = "IN" + INC = "INC" + IMS = "IMS" + IFLT = "IFLT" + INFX = "INFX" + INGT = "INGT" + INPAY = "INPAY" + INSANE = "INSANE" + INXT = "INXT" + IFT = "IFT" + INV = "INV" + IVZ = "IVZ" + ILT = "ILT" + IONX = "IONX" + ISL = "ISL" + ITI = "ITI" + ING = "ING" + IEC = "IEC" + IW = "IW" + IXC = "IXC" + IXT = "IXT" + JPC = "JPC" + JANE = "JANE" + JWL = "JWL" + JIF = "JIF" + JOBS = "JOBS" + JOCKER = "JOCKER" + JW = "JW" + JOK = "JOK" + XJO = "XJO" + KGB = "KGB" + KARMC = "KARMC" + KARMA = "KARMA" + KASHH = "KASHH" + KAT = "KAT" + KC = "KC" + KIDS = "KIDS" + KIN = "KIN" + KISS = "KISS" + KOBO = "KOBO" + TP1 = "TP1" + KRAK = "KRAK" + KGC = "KGC" + KTK = "KTK" + KR = "KR" + KUBO = "KUBO" + KURT = "KURT" + KUSH = "KUSH" + LANA = "LANA" + LTH = "LTH" + LAZ = "LAZ" + LEA = "LEA" + LEAF = "LEAF" + LENIN = "LENIN" + LEPEN = "LEPEN" + LIR = "LIR" + LVG = "LVG" + LGBTQ = "LGBTQ" + LHC = "LHC" + EXT = "EXT" + LBTC = "LBTC" + LSD = "LSD" + LIMX = "LIMX" + LTD = "LTD" + LINDA = "LINDA" + LKC = "LKC" + LBTCX = "LBTCX" + LCC = "LCC" + LTCU = "LTCU" + LTCR = "LTCR" + LDOGE = "LDOGE" + LTS = "LTS" + LIV = "LIV" + LIZI = "LIZI" + LOC = "LOC" + LOCX = "LOCX" + LOOK = "LOOK" + LOOT = "LOOT" + XLTCG = "XLTCG" + BASH = "BASH" + LUCKY = "LUCKY" + L7S = "L7S" + LDM = "LDM" + LUMI = "LUMI" + LUNA = "LUNA" + LC = "LC" + LUX = "LUX" + MCRN = "MCRN" + XMG = "XMG" + MMXIV = "MMXIV" + MAT = "MAT" + MAO = "MAO" + MAPC = "MAPC" + MRB = "MRB" + MXT = "MXT" + MARV = "MARV" + MARX = "MARX" + MCAR = "MCAR" + MM = "MM" + MVC = "MVC" + MAVRO = "MAVRO" + MAX = "MAX" + MAZE = "MAZE" + MBIT = "MBIT" + MCOIN = "MCOIN" + MPRO = "MPRO" + XMS = "XMS" + MLITE = "MLITE" + MLNC = "MLNC" + MENTAL = "MENTAL" + MERGEC = "MERGEC" + MTLMC3 = "MTLMC3" + METAL = "METAL" + MUU = "MUU" + MILO = "MILO" + MND = "MND" + XMINE = "XMINE" + MNM = "MNM" + XNM = "XNM" + MIRO = "MIRO" + MIS = "MIS" + MMXVI = "MMXVI" + MOIN = "MOIN" + MOJO = "MOJO" + TAB = "TAB" + MONETA = "MONETA" + MUE = "MUE" + MONEY = "MONEY" + MRP = "MRP" + MOTO = "MOTO" + MULTI = "MULTI" + MST = "MST" + MVR = "MVR" + MYSTIC = "MYSTIC" + WISH = "WISH" + NKT = "NKT" + NAT = "NAT" + ENAU = "ENAU" + NEBU = "NEBU" + NEF = "NEF" + NBIT = "NBIT" + NETKO = "NETKO" + NTM = "NTM" + NETC = "NETC" + NRC = "NRC" + NTK = "NTK" + NTRN = "NTRN" + NEVA = "NEVA" + NIC = "NIC" + NKC = "NKC" + NYC = "NYC" + NZC = "NZC" + NICE = "NICE" + NDOGE = "NDOGE" + XTR = "XTR" + N2O = "N2O" + NIXON = "NIXON" + NOC = "NOC" + NODC = "NODC" + NODES = "NODES" + NODX = "NODX" + NLC = "NLC" + NLC2 = "NLC2" + NOO = "NOO" + NVC = "NVC" + NPC = "NPC" + NUBIS = "NUBIS" + NUKE = "NUKE" + N7 = "N7" + NUM = "NUM" + NMR = "NMR" + NXE = "NXE" + OBS = "OBS" + OCEAN = "OCEAN" + OCOW = "OCOW" + EIGHT88 = "888" + OCC = "OCC" + OK = "OK" + ODNT = "ODNT" + FLAV = "FLAV" + OLIT = "OLIT" + OLYMP = "OLYMP" + OMA = "OMA" + OMC = "OMC" + ONEK = "ONEK" + ONX = "ONX" + XPO = "XPO" + OPAL = "OPAL" + OTN = "OTN" + OP = "OP" + OPES = "OPES" + OPTION = "OPTION" + ORLY = "ORLY" + OS76 = "OS76" + OZC = "OZC" + P7C = "P7C" + PAC = "PAC" + PAK = "PAK" + PAL = "PAL" + PND = "PND" + PINKX = "PINKX" + POPPY = "POPPY" + DUO = "DUO" + PARA = "PARA" + PKB = "PKB" + GENE = "GENE" + PARTY = "PARTY" + PYN = "PYN" + XPY = "XPY" + CON = "CON" + PAYP = "PAYP" + GUESS = "GUESS" + PEN = "PEN" + PTA = "PTA" + PEO = "PEO" + PSB = "PSB" + XPD = "XPD" + PXL = "PXL" + PHR = "PHR" + PIE = "PIE" + PIO = "PIO" + PIPR = "PIPR" + SKULL = "SKULL" + PLANET = "PLANET" + PNC = "PNC" + XPTX = "XPTX" + PLNC = "PLNC" + XPS = "XPS" + POKE = "POKE" + PLBT = "PLBT" + POM = "POM" + PONZ2 = "PONZ2" + PONZI = "PONZI" + XSP = "XSP" + XPC = "XPC" + PEX = "PEX" + TRON = "TRON" + POST = "POST" + POSW = "POSW" + PWR = "PWR" + POWER = "POWER" + PRE = "PRE" + PRS = "PRS" + PXI = "PXI" + PEXT = "PEXT" + PRIMU = "PRIMU" + PRX = "PRX" + PRM = "PRM" + PRIX = "PRIX" + XPRO = "XPRO" + PCM = "PCM" + PROC = "PROC" + NANOX = "NANOX" + VRP = "VRP" + PTY = "PTY" + PSI = "PSI" + PSY = "PSY" + PULSE = "PULSE" + PUPA = "PUPA" + PURE = "PURE" + VIDZ = "VIDZ" + PUTIN = "PUTIN" + PX = "PX" + QTM = "QTM" + QTZ = "QTZ" + QBC = "QBC" + XQN = "XQN" + RBBT = "RBBT" + RAC = "RAC" + RADI = "RADI" + RAD = "RAD" + RAI = "RAI" + XRA = "XRA" + RATIO = "RATIO" + REA = "REA" + RCX = "RCX" + REE = "REE" + REC = "REC" + RMS = "RMS" + RBIT = "RBIT" + RNC = "RNC" + REV = "REV" + RH = "RH" + XRL = "XRL" + RICE = "RICE" + RICHX = "RICHX" + RID = "RID" + RIDE = "RIDE" + RBT = "RBT" + RING = "RING" + RIO = "RIO" + RISE = "RISE" + ROCKET = "ROCKET" + RPC = "RPC" + ROS = "ROS" + ROYAL = "ROYAL" + RSGP = "RSGP" + RBIES = "RBIES" + RUBIT = "RUBIT" + RBY = "RBY" + RUC = "RUC" + RUPX = "RUPX" + RUP = "RUP" + RUST = "RUST" + SFE = "SFE" + SLS = "SLS" + SMSR = "SMSR" + RONIN = "RONIN" + STV = "STV" + HIFUN = "HIFUN" + MAD = "MAD" + SANDG = "SANDG" + STO = "STO" + SCAN = "SCAN" + SCITW = "SCITW" + SCRPT = "SCRPT" + SCRT = "SCRT" + SED = "SED" + SEEDS = "SEEDS" + B2X = "B2X" + SEL = "SEL" + SLFI = "SLFI" + SMBR = "SMBR" + SEN = "SEN" + SENT = "SENT" + SRNT = "SRNT" + SEV = "SEV" + SP = "SP" + SXC = "SXC" + GELD = "GELD" + SHDW = "SHDW" + SDC = "SDC" + SAK = "SAK" + SHRP = "SHRP" + SHELL = "SHELL" + SH = "SH" + SHORTY = "SHORTY" + SHREK = "SHREK" + SHRM = "SHRM" + SIB = "SIB" + SIGT = "SIGT" + SLCO = "SLCO" + SIGU = "SIGU" + SIX = "SIX" + SJW = "SJW" + SKB = "SKB" + SW = "SW" + SLEEP = "SLEEP" + SLING = "SLING" + SMART = "SMART" + SMC = "SMC" + SMF = "SMF" + SOCC = "SOCC" + SCL = "SCL" + SDAO = "SDAO" + SOLAR = "SOLAR" + SOLO = "SOLO" + SCT = "SCT" + SONG = "SONG" + ALTCOM = "ALTCOM" + SPHTX = "SPHTX" + SPC = "SPC" + SPACE = "SPACE" + SBT = "SBT" + SPEC = "SPEC" + SPX = "SPX" + SCS = "SCS" + SPORT = "SPORT" + SPT = "SPT" + SPR = "SPR" + SPEX = "SPEX" + SQL = "SQL" + SBIT = "SBIT" + STHR = "STHR" + STALIN = "STALIN" + STAR = "STAR" + STA = "STA" + START = "START" + STP = "STP" + PNK = "PNK" + STEPS = "STEPS" + STK = "STK" + STONK = "STONK" + STS = "STS" + STRP = "STRP" + STY = "STY" + XMT = "XMT" + SSTC = "SSTC" + SUPER = "SUPER" + SRND = "SRND" + STRB = "STRB" + M1 = "M1" + SPM = "SPM" + BUCKS = "BUCKS" + TOKEN = "TOKEN" + SWT = "SWT" + SWEET = "SWEET" + SWING = "SWING" + CHSB = "CHSB" + SIC = "SIC" + SDP = "SDP" + XSY = "XSY" + SYNX = "SYNX" + SNRG = "SNRG" + TAG = "TAG" + TAGR = "TAGR" + TAJ = "TAJ" + TAK = "TAK" + TAKE = "TAKE" + TAM = "TAM" + XTO = "XTO" + TAP = "TAP" + TLE = "TLE" + TSE = "TSE" + TLEX = "TLEX" + TAXI = "TAXI" + TCN = "TCN" + TDFB = "TDFB" + TEAM = "TEAM" + TECH = "TECH" + TEC = "TEC" + TEK = "TEK" + TB = "TB" + TLX = "TLX" + TELL = "TELL" + TENNET = "TENNET" + TES = "TES" + TGS = "TGS" + XVE = "XVE" + TCR = "TCR" + GCC = "GCC" + MAY = "MAY" + THOM = "THOM" + TIA = "TIA" + TIDE = "TIDE" + TIE = "TIE" + TIT = "TIT" + TTC = "TTC" + TODAY = "TODAY" + TBX = "TBX" + TDS = "TDS" + TLOSH = "TLOSH" + TOKC = "TOKC" + TMRW = "TMRW" + TOOL = "TOOL" + TCX = "TCX" + TOT = "TOT" + TX = "TX" + TRANSF = "TRANSF" + TRAP = "TRAP" + TBCX = "TBCX" + TRICK = "TRICK" + TPG = "TPG" + TFL = "TFL" + TRUMP = "TRUMP" + TNG = "TNG" + TUR = "TUR" + TWERK = "TWERK" + TWIST = "TWIST" + TWO = "TWO" + UCASH = "UCASH" + UAE = "UAE" + XBU = "XBU" + UBQ = "UBQ" + U = "U" + UDOWN = "UDOWN" + GAIN = "GAIN" + USC = "USC" + UMC = "UMC" + UNF = "UNF" + UNIFY = "UNIFY" + USDE = "USDE" + UBTC = "UBTC" + UIS = "UIS" + UNIT = "UNIT" + UNI = "UNI" + UXC = "UXC" + URC = "URC" + XUP = "XUP" + UFR = "UFR" + URO = "URO" + UTLE = "UTLE" + VAL = "VAL" + VPRC = "VPRC" + VAPOR = "VAPOR" + VCOIN = "VCOIN" + VEC = "VEC" + VEC2 = "VEC2" + VLT = "VLT" + VENE = "VENE" + VNTX = "VNTX" + VTN = "VTN" + CRED = "CRED" + VERS = "VERS" + VTX = "VTX" + VTY = "VTY" + VIP = "VIP" + VISIO = "VISIO" + VK = "VK" + VOL = "VOL" + VOYA = "VOYA" + VPN = "VPN" + XVS = "XVS" + VTL = "VTL" + VULC = "VULC" + VVI = "VVI" + WGR = "WGR" + WAM = "WAM" + WARP = "WARP" + WASH = "WASH" + WGO = "WGO" + WAY = "WAY" + WCASH = "WCASH" + WEALTH = "WEALTH" + WEEK = "WEEK" + WHO = "WHO" + WIC = "WIC" + WBB = "WBB" + WINE = "WINE" + WINK = "WINK" + WISC = "WISC" + WITCH = "WITCH" + WMC = "WMC" + WOMEN = "WOMEN" + WOK = "WOK" + WRT = "WRT" + XCO = "XCO" + X2 = "X2" + XNX = "XNX" + XAU = "XAU" + XAV = "XAV" + XDE2 = "XDE2" + XDE = "XDE" + XIOS = "XIOS" + XOC = "XOC" + XSSX = "XSSX" + XBY = "XBY" + YAC = "YAC" + YMC = "YMC" + YAY = "YAY" + YBC = "YBC" + YES = "YES" + YOB2X = "YOB2X" + YOVI = "YOVI" + ZYD = "ZYD" + ZECD = "ZECD" + ZEIT = "ZEIT" + ZENI = "ZENI" + ZET2 = "ZET2" + ZET = "ZET" + ZMC = "ZMC" + ZIRK = "ZIRK" + ZLQ = "ZLQ" + ZNE = "ZNE" + ZONTO = "ZONTO" + ZOOM = "ZOOM" + ZRC = "ZRC" + ZUR = "ZUR" + ZB = "ZB" + QC = "QC" + HLC = "HLC" + SAFE = "SAFE" + BTN = "BTN" + CDC = "CDC" + DDM = "DDM" + HOTC = "HOTC" + BDS = "BDS" + AAA = "AAA" + XWC = "XWC" + PDX = "PDX" + SLT = "SLT" + HPY = "HPY" + XXBT = "XXBT" // BTC, but XXBT instead + XDG = "XDG" // DOGE + HKD = "HKD" // Hong Kong Dollar + AUD = "AUD" // Australian Dollar + USD = "USD" // United States Dollar + ZUSD = "ZUSD" // United States Dollar, but with a Z in front of it + EUR = "EUR" // Euro + ZEUR = "ZEUR" // Euro, but with a Z in front of it + CAD = "CAD" // Canadaian Dollar + ZCAD = "ZCAD" // Canadaian Dollar, but with a Z in front of it + SGD = "SGD" // Singapore Dollar + RUB = "RUB" // RUssian ruBle + RUR = "RUR" // RUssian Ruble + PLN = "PLN" // Polish złoty + TRY = "TRY" // Turkish lira + UAH = "UAH" // Ukrainian hryvnia + JPY = "JPY" // Japanese yen + ZJPY = "ZJPY" // Japanese yen, but with a Z in front of it +) + // symbols map holds the currency name and symbol mappings var symbols = map[string]string{ "ALL": "Lek", diff --git a/exchanges/alphapoint/alphapoint_wrapper.go b/exchanges/alphapoint/alphapoint_wrapper.go index 5f67d7b6..6c0cb78f 100644 --- a/exchanges/alphapoint/alphapoint_wrapper.go +++ b/exchanges/alphapoint/alphapoint_wrapper.go @@ -176,3 +176,8 @@ func (a *Alphapoint) WithdrawFiatExchangeFunds(currency pair.CurrencyItem, amoun func (a *Alphapoint) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (a *Alphapoint) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return 0, errors.New("not yet implemented") +} diff --git a/exchanges/anx/anx.go b/exchanges/anx/anx.go index 0f88a2dc..d98a522c 100644 --- a/exchanges/anx/anx.go +++ b/exchanges/anx/anx.go @@ -8,6 +8,8 @@ import ( "strconv" "time" + "github.com/thrasher-/gocryptotrader/currency/symbol" + "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/exchanges" @@ -44,8 +46,8 @@ type ANX struct { func (a *ANX) SetDefaults() { a.Name = "ANX" a.Enabled = false - a.TakerFee = 0.6 - a.MakerFee = 0.3 + a.TakerFee = 0.02 + a.MakerFee = 0.01 a.Verbose = false a.RESTPollingDelay = 10 a.RequestCurrencyPairFormat.Delimiter = "" @@ -118,14 +120,6 @@ func (a *ANX) GetCurrencies() (CurrenciesStore, error) { return result.CurrenciesResponse, nil } -// GetFee returns maker or taker fees -func (a *ANX) GetFee(maker bool) float64 { - if maker { - return a.MakerFee - } - return a.TakerFee -} - // GetTicker returns the current ticker func (a *ANX) GetTicker(currency string) (Ticker, error) { var ticker Ticker @@ -405,3 +399,47 @@ func (a *ANX) SendAuthenticatedHTTPRequest(path string, params map[string]interf return a.SendPayload("POST", a.APIUrl+path, headers, bytes.NewBuffer(PayloadJSON), result, true, a.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (a *ANX) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = a.calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + case exchange.CryptocurrencyWithdrawalFee: + fee = getCryptocurrencyWithdrawalFee(feeBuilder.FirstCurrency) + case exchange.InternationalBankWithdrawalFee: + fee = getInternationalBankWithdrawalFee(feeBuilder.CurrencyItem, feeBuilder.Amount) + } + if fee < 0 { + fee = 0 + } + return fee, nil +} + +func (a *ANX) calculateTradingFee(purchasePrice, amount float64, isMaker bool) float64 { + var fee float64 + + if isMaker { + fee = a.MakerFee * amount * purchasePrice + } else { + fee = a.TakerFee * amount * purchasePrice + } + + return fee +} + +func getCryptocurrencyWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} + +func getInternationalBankWithdrawalFee(currency string, amount float64) float64 { + var fee float64 + + if currency == symbol.HKD { + fee = 250 + (WithdrawalFees[currency] * amount) + } + //TODO, other fiat currencies require consultation with ANXPRO + return fee +} diff --git a/exchanges/anx/anx_test.go b/exchanges/anx/anx_test.go index 10eb9dfe..955c8c4b 100644 --- a/exchanges/anx/anx_test.go +++ b/exchanges/anx/anx_test.go @@ -4,32 +4,34 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) -var anx ANX +var a ANX func TestSetDefaults(t *testing.T) { - anx.SetDefaults() + a.SetDefaults() - if anx.Name != "ANX" { + if a.Name != "ANX" { t.Error("Test Failed - ANX SetDefaults() incorrect values set") } - if anx.Enabled != false { + if a.Enabled != false { t.Error("Test Failed - ANX SetDefaults() incorrect values set") } - if anx.TakerFee != 0.6 { + if a.TakerFee != 0.02 { t.Error("Test Failed - ANX SetDefaults() incorrect values set") } - if anx.MakerFee != 0.3 { + if a.MakerFee != 0.01 { t.Error("Test Failed - ANX SetDefaults() incorrect values set") } - if anx.Verbose != false { + if a.Verbose != false { t.Error("Test Failed - ANX SetDefaults() incorrect values set") } - if anx.Websocket.IsEnabled() != false { + if a.Websocket.IsEnabled() != false { t.Error("Test Failed - ANX SetDefaults() incorrect values set") } - if anx.RESTPollingDelay != 10 { + if a.RESTPollingDelay != 10 { t.Error("Test Failed - ANX SetDefaults() incorrect values set") } } @@ -41,67 +43,56 @@ func TestSetup(t *testing.T) { if err != nil { t.Error("Test Failed - ANX Setup() init error") } - anx.Setup(anxConfig) + a.Setup(anxConfig) - if anx.Enabled != true { + if a.Enabled != true { t.Error("Test Failed - ANX Setup() incorrect values set") } - if anx.AuthenticatedAPISupport != false { + if a.AuthenticatedAPISupport != false { t.Error("Test Failed - ANX Setup() incorrect values set") } - if len(anx.APIKey) != 0 { + if len(a.APIKey) != 0 { t.Error("Test Failed - ANX Setup() incorrect values set") } - if len(anx.APISecret) != 0 { + if len(a.APISecret) != 0 { t.Error("Test Failed - ANX Setup() incorrect values set") } - if anx.RESTPollingDelay != 10 { + if a.RESTPollingDelay != 10 { t.Error("Test Failed - ANX Setup() incorrect values set") } - if anx.Verbose != false { + if a.Verbose != false { t.Error("Test Failed - ANX Setup() incorrect values set") } - if anx.Websocket.IsEnabled() != false { + if a.Websocket.IsEnabled() != false { t.Error("Test Failed - ANX Setup() incorrect values set") } - if len(anx.BaseCurrencies) <= 0 { + if len(a.BaseCurrencies) <= 0 { t.Error("Test Failed - ANX Setup() incorrect values set") } - if len(anx.AvailablePairs) <= 0 { + if len(a.AvailablePairs) <= 0 { t.Error("Test Failed - ANX Setup() incorrect values set") } - if len(anx.EnabledPairs) <= 0 { + if len(a.EnabledPairs) <= 0 { t.Error("Test Failed - ANX Setup() incorrect values set") } } func TestGetCurrencies(t *testing.T) { - _, err := anx.GetCurrencies() + _, err := a.GetCurrencies() if err != nil { t.Fatalf("Test failed. TestGetCurrencies failed. Err: %s", err) } } func TestGetTradablePairs(t *testing.T) { - _, err := anx.GetTradablePairs() + _, err := a.GetTradablePairs() if err != nil { t.Fatalf("Test failed. TestGetTradablePairs failed. Err: %s", err) } } -func TestGetFee(t *testing.T) { - makerFeeExpected, takerFeeExpected := 0.3, 0.6 - - if anx.GetFee(true) != makerFeeExpected { - t.Error("Test Failed - ANX GetFee() incorrect return value") - } - if anx.GetFee(false) != takerFeeExpected { - t.Error("Test Failed - ANX GetFee() incorrect return value") - } -} - func TestGetTicker(t *testing.T) { - ticker, err := anx.GetTicker("BTCUSD") + ticker, err := a.GetTicker("BTCUSD") if err != nil { t.Errorf("Test Failed - ANX GetTicker() error: %s", err) } @@ -111,7 +102,7 @@ func TestGetTicker(t *testing.T) { } func TestGetDepth(t *testing.T) { - ticker, err := anx.GetDepth("BTCUSD") + ticker, err := a.GetDepth("BTCUSD") if err != nil { t.Errorf("Test Failed - ANX GetDepth() error: %s", err) } @@ -121,7 +112,7 @@ func TestGetDepth(t *testing.T) { } func TestGetAPIKey(t *testing.T) { - apiKey, apiSecret, err := anx.GetAPIKey("userName", "passWord", "", "1337") + apiKey, apiSecret, err := a.GetAPIKey("userName", "passWord", "", "1337") if err == nil { t.Error("Test Failed - ANX GetAPIKey() Incorrect") } @@ -132,3 +123,87 @@ func TestGetAPIKey(t *testing.T) { t.Error("Test Failed - ANX GetAPIKey() Incorrect") } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + } +} + +func TestGetFee(t *testing.T) { + a.SetDefaults() + TestSetup(t) + + var feeBuilder = setFeeBuilder() + + // CryptocurrencyTradeFee Basic + if resp, err := a.GetFee(feeBuilder); resp != float64(0.02) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := a.GetFee(feeBuilder); resp != float64(20000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(20000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := a.GetFee(feeBuilder); resp != float64(0.01) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.01), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := a.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := a.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := a.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := a.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := a.GetFee(feeBuilder); resp != float64(250.01) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(250.01), resp) + t.Error(err) + } +} diff --git a/exchanges/anx/anx_types.go b/exchanges/anx/anx_types.go index a82c64ec..36dc3317 100644 --- a/exchanges/anx/anx_types.go +++ b/exchanges/anx/anx_types.go @@ -1,5 +1,7 @@ package anx +import "github.com/thrasher-/gocryptotrader/currency/symbol" + // Currency holds the currency information type Currency struct { Decimals int `json:"decimals"` @@ -146,3 +148,16 @@ type Depth struct { Bids []DepthItem `json:"bids"` } `json:"data"` } + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change +var WithdrawalFees = map[string]float64{ + symbol.BTC: 0.002, + symbol.DOGE: 0.1, + symbol.ETH: 0.005, + symbol.GNT: 0.001, + symbol.LTC: 0.02, + symbol.OAX: 0.001, + symbol.XRP: 1, + symbol.HKD: 0.01, +} diff --git a/exchanges/anx/anx_wrapper.go b/exchanges/anx/anx_wrapper.go index 8eb24939..d0d97e8c 100644 --- a/exchanges/anx/anx_wrapper.go +++ b/exchanges/anx/anx_wrapper.go @@ -249,3 +249,8 @@ func (a *ANX) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Currenc func (a *ANX) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (a *ANX) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return a.GetFee(feeBuilder) +} diff --git a/exchanges/binance/binance.go b/exchanges/binance/binance.go index b649e536..b0cf80b8 100644 --- a/exchanges/binance/binance.go +++ b/exchanges/binance/binance.go @@ -652,3 +652,48 @@ func (b *Binance) SetValues() { TimeIntervalMonth, } } + +// GetFee returns an estimate of fee based on type of transaction +func (b *Binance) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + multiplier, err := b.getMultiplier(feeBuilder.IsMaker) + if err != nil { + return 0, err + } + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, multiplier) + case exchange.CryptocurrencyWithdrawalFee: + fee = getCryptocurrencyWithdrawalFee(feeBuilder.FirstCurrency, feeBuilder.PurchasePrice, feeBuilder.Amount) + } + if fee < 0 { + fee = 0 + } + return fee, nil +} + +// getMultiplier retrieves account based taker/maker fees +func (b *Binance) getMultiplier(isMaker bool) (float64, error) { + var multiplier float64 + account, err := b.GetAccount() + if err != nil { + return 0, err + } + if isMaker { + multiplier = float64(account.MakerCommission) + } else { + multiplier = float64(account.TakerCommission) + } + return multiplier, nil +} + +// calculateTradingFee returns the fee for trading any currency on Bittrex +func calculateTradingFee(purchasePrice, amount, multiplier float64) float64 { + return (multiplier / 100) * purchasePrice * amount +} + +// getCryptocurrencyWithdrawalFee returns the fee for withdrawing from the exchange +func getCryptocurrencyWithdrawalFee(currency string, purchasePrice, amount float64) float64 { + return WithdrawalFees[currency] +} diff --git a/exchanges/binance/binance_test.go b/exchanges/binance/binance_test.go index 0ca6d3f1..ad25453b 100644 --- a/exchanges/binance/binance_test.go +++ b/exchanges/binance/binance_test.go @@ -3,7 +3,10 @@ package binance import ( "testing" + "github.com/thrasher-/gocryptotrader/currency/symbol" + "github.com/thrasher-/gocryptotrader/config" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) // Please supply your own keys here for due diligence testing @@ -29,7 +32,6 @@ func TestSetup(t *testing.T) { binanceConfig.AuthenticatedAPISupport = true binanceConfig.APIKey = testAPIKey binanceConfig.APISecret = testAPISecret - b.Setup(binanceConfig) } @@ -205,3 +207,112 @@ func TestAllOrders(t *testing.T) { t.Error("Test Failed - Binance AllOrders() error", err) } } + +func TestGetAccount(t *testing.T) { + if testAPIKey == "" || testAPISecret == "" { + t.Skip() + } + t.Parallel() + b.SetDefaults() + TestSetup(t) + account, err := b.GetAccount() + if err != nil { + t.Fatal("Test Failed - Binance GetAccount() error", err) + } + if account.MakerCommission <= 0 { + t.Fatalf("Test Failed. Expected > 0, Recieved %d", account.MakerCommission) + } + if account.TakerCommission <= 0 { + t.Fatalf("Test Failed. Expected > 0, Recieved %d", account.TakerCommission) + } + + t.Logf("Current makerFee: %d", account.MakerCommission) + t.Logf("Current takerFee: %d", account.TakerCommission) +} + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + } +} + +func TestGetFee(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var feeBuilder = setFeeBuilder() + + if testAPIKey != "" || testAPISecret != "" { + // CryptocurrencyTradeFee Basic + if resp, err := b.GetFee(feeBuilder); resp != float64(0.1) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(100000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(100000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := b.GetFee(feeBuilder); resp != float64(0.1) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.1), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0.0005) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0005), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/binance/binance_types.go b/exchanges/binance/binance_types.go index 8fe6b1a8..6e1e886b 100644 --- a/exchanges/binance/binance_types.go +++ b/exchanges/binance/binance_types.go @@ -2,6 +2,8 @@ package binance import ( "encoding/json" + + "github.com/thrasher-/gocryptotrader/currency/symbol" ) // Response holds basic binance api response data @@ -432,3 +434,172 @@ var ( TimeIntervalWeek = TimeInterval("1w") TimeIntervalMonth = TimeInterval("1M") ) + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change +var WithdrawalFees = map[string]float64{ + symbol.BNB: 0.13, + symbol.BTC: 0.0005, + symbol.NEO: 0, + symbol.ETH: 0.01, + symbol.LTC: 0.001, + symbol.QTUM: 0.01, + symbol.EOS: 0.1, + symbol.SNT: 35, + symbol.BNT: 1, + symbol.GAS: 0, + symbol.BCC: 0.001, + symbol.BTM: 5, + symbol.USDT: 3.4, + symbol.HCC: 0.0005, + symbol.OAX: 6.5, + symbol.DNT: 54, + symbol.MCO: 0.31, + symbol.ICN: 3.5, + symbol.ZRX: 1.9, + symbol.OMG: 0.4, + symbol.WTC: 0.5, + symbol.LRC: 12.3, + symbol.LLT: 67.8, + symbol.YOYO: 1, + symbol.TRX: 1, + symbol.STRAT: 0.1, + symbol.SNGLS: 54, + symbol.BQX: 3.9, + symbol.KNC: 3.5, + symbol.SNM: 25, + symbol.FUN: 86, + symbol.LINK: 4, + symbol.XVG: 0.1, + symbol.CTR: 35, + symbol.SALT: 2.3, + symbol.MDA: 2.3, + symbol.IOTA: 0.5, + symbol.SUB: 11.4, + symbol.ETC: 0.01, + symbol.MTL: 2, + symbol.MTH: 45, + symbol.ENG: 2.2, + symbol.AST: 14.4, + symbol.DASH: 0.002, + symbol.BTG: 0.001, + symbol.EVX: 2.8, + symbol.REQ: 29.9, + symbol.VIB: 30, + symbol.POWR: 8.2, + symbol.ARK: 0.2, + symbol.XRP: 0.25, + symbol.MOD: 2, + symbol.ENJ: 26, + symbol.STORJ: 5.1, + symbol.KMD: 0.002, + symbol.RCN: 47, + symbol.NULS: 0.01, + symbol.RDN: 2.5, + symbol.XMR: 0.04, + symbol.DLT: 19.8, + symbol.AMB: 8.9, + symbol.BAT: 8, + symbol.ZEC: 0.005, + symbol.BCPT: 14.5, + symbol.ARN: 3, + symbol.GVT: 0.13, + symbol.CDT: 81, + symbol.GXS: 0.3, + symbol.POE: 134, + symbol.QSP: 36, + symbol.BTS: 1, + symbol.XZC: 0.02, + symbol.LSK: 0.1, + symbol.TNT: 47, + symbol.FUEL: 79, + symbol.MANA: 18, + symbol.BCD: 0.01, + symbol.DGD: 0.04, + symbol.ADX: 6.3, + symbol.ADA: 1, + symbol.PPT: 0.41, + symbol.CMT: 12, + symbol.XLM: 0.01, + symbol.CND: 58, + symbol.LEND: 84, + symbol.WABI: 6.6, + symbol.SBTC: 0.0005, + symbol.BCX: 0.5, + symbol.WAVES: 0.002, + symbol.TNB: 139, + symbol.GTO: 20, + symbol.ICX: 0.02, + symbol.OST: 32, + symbol.ELF: 3.9, + symbol.AION: 3.2, + symbol.CVC: 10.9, + symbol.REP: 0.2, + symbol.GNT: 8.9, + symbol.DATA: 37, + symbol.ETF: 1, + symbol.BRD: 3.8, + symbol.NEBL: 0.01, + symbol.VIBE: 17.3, + symbol.LUN: 0.36, + symbol.CHAT: 60.7, + symbol.RLC: 3.4, + symbol.INS: 3.5, + symbol.IOST: 105.6, + symbol.STEEM: 0.01, + symbol.NANO: 0.01, + symbol.AE: 1.3, + symbol.VIA: 0.01, + symbol.BLZ: 10.3, + symbol.SYS: 1, + symbol.NCASH: 247.6, + symbol.POA: 0.01, + symbol.ONT: 1, + symbol.ZIL: 37.2, + symbol.STORM: 152, + symbol.XEM: 4, + symbol.WAN: 0.1, + symbol.WPR: 43.4, + symbol.QLC: 1, + symbol.GRS: 0.2, + symbol.CLOAK: 0.02, + symbol.LOOM: 11.9, + symbol.BCN: 1, + symbol.TUSD: 1.35, + symbol.ZEN: 0.002, + symbol.SKY: 0.01, + symbol.THETA: 24, + symbol.IOTX: 90.5, + symbol.QKC: 24.6, + symbol.AGI: 29.81, + symbol.NXS: 0.02, + symbol.SC: 0.1, + symbol.EON: 10, + symbol.NPXS: 897, + symbol.KEY: 223, + symbol.NAS: 0.1, + symbol.ADD: 100, + symbol.MEETONE: 300, + symbol.ATD: 100, + symbol.MFT: 175, + symbol.EOP: 5, + symbol.DENT: 596, + symbol.IQ: 50, + symbol.ARDR: 2, + symbol.HOT: 1210, + symbol.VET: 100, + symbol.DOCK: 68, + symbol.POLY: 7, + symbol.VTHO: 21, + symbol.ONG: 0.1, + symbol.PHX: 1, + symbol.HC: 0.005, + symbol.GO: 0.01, + symbol.PAX: 1.4, + symbol.EDO: 1.3, + symbol.WINGS: 8.9, + symbol.NAV: 0.2, + symbol.TRIG: 49.1, + symbol.APPC: 12.4, + symbol.PIVX: 0.02, +} diff --git a/exchanges/binance/binance_wrapper.go b/exchanges/binance/binance_wrapper.go index dea21b28..63542946 100644 --- a/exchanges/binance/binance_wrapper.go +++ b/exchanges/binance/binance_wrapper.go @@ -194,3 +194,8 @@ func (b *Binance) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Cur func (b *Binance) GetWebsocket() (*exchange.Websocket, error) { return b.Websocket, nil } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (b *Binance) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return b.GetFee(feeBuilder) +} diff --git a/exchanges/bitfinex/bitfinex.go b/exchanges/bitfinex/bitfinex.go index 28dedc99..10822135 100644 --- a/exchanges/bitfinex/bitfinex.go +++ b/exchanges/bitfinex/bitfinex.go @@ -489,36 +489,19 @@ func (b *Bitfinex) GetSymbolsDetails() ([]SymbolDetails, error) { } // GetAccountInfo returns information about your account incl. trading fees -func (b *Bitfinex) GetAccountInfo() (AccountInfo, error) { +func (b *Bitfinex) GetAccountInfo() ([]AccountInfo, error) { - var result AccountInfo - - var response []interface{} - err := b.SendAuthenticatedHTTPRequest("POST", bitfinexAccountInfo, nil, &response) + var responses []AccountInfo + err := b.SendAuthenticatedHTTPRequest("POST", bitfinexAccountInfo, nil, &responses) if err != nil { - return result, err + return responses, err } - result = AccountInfo{} - result.MakerFees = response[0].(map[string]interface{})["maker_fees"].(string) - result.TakerFees = response[0].(map[string]interface{})["taker_fees"].(string) - - feeslist := response[0].(map[string]interface{})["fees"].([]interface{}) - for _, v := range feeslist { - item := v.(map[string]interface{}) - - result.Fees = append(result.Fees, AccountInfoFees{ - Pairs: item["pairs"].(string), - MakerFees: item["maker_fees"].(string), - TakerFees: item["taker_fees"].(string), - }) - } - - return result, nil + return responses, nil } -// GetAccountFees - NOT YET IMPLEMENTED +// GetAccountFees - Gets all fee rates for all currencies func (b *Bitfinex) GetAccountFees() (AccountFees, error) { response := AccountFees{} @@ -903,7 +886,7 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[ } request := make(map[string]interface{}) - request["request"] = fmt.Sprintf("/v%s/%s", bitfinexAPIVersion, path) + request["request"] = fmt.Sprintf("%s%s", bitfinexAPIVersion, path) request["nonce"] = b.Nonce.String() if params != nil { @@ -934,3 +917,83 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(method, path string, params map[ } return nil } + +// GetFee returns an estimate of fee based on type of transaction +func (b *Bitfinex) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + accountInfos, err := b.GetAccountInfo() + if err != nil { + return 0, err + } + fee, err = b.CalculateTradingFee(accountInfos, feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.FirstCurrency, feeBuilder.IsMaker) + if err != nil { + return 0, err + } + case exchange.CyptocurrencyDepositFee: + //TODO: fee is charged when < $1000USD is transferred, need to infer value in some way + fee = 0 + case exchange.CryptocurrencyWithdrawalFee: + accountFees, err := b.GetAccountFees() + if err != nil { + return 0, err + } + fee, err = b.GetCryptocurrencyWithdrawalFee(feeBuilder.FirstCurrency, accountFees) + if err != nil { + return 0, err + } + case exchange.InternationalBankDepositFee: + fee = getInternationalBankDepositFee(feeBuilder.Amount) + case exchange.InternationalBankWithdrawalFee: + fee = getInternationalBankWithdrawalFee(feeBuilder.Amount) + } + if fee < 0 { + fee = 0 + } + return fee, nil +} + +// GetCryptocurrencyWithdrawalFee returns an estimate of fee based on type of transaction +func (b *Bitfinex) GetCryptocurrencyWithdrawalFee(currency string, accountFees AccountFees) (fee float64, err error) { + switch accountFees.Withdraw[currency].(type) { + case string: + fee, err = strconv.ParseFloat(accountFees.Withdraw[currency].(string), 64) + if err != nil { + return 0, err + } + case float64: + fee = accountFees.Withdraw[currency].(float64) + } + + return fee, nil +} + +func getInternationalBankDepositFee(amount float64) float64 { + return 0.001 * amount +} + +func getInternationalBankWithdrawalFee(amount float64) float64 { + return 0.001 * amount +} + +// CalculateTradingFee returns an estimate of fee based on type of whether is maker or taker fee +func (b *Bitfinex) CalculateTradingFee(accountInfos []AccountInfo, purchasePrice, amount float64, currency string, isMaker bool) (fee float64, err error) { + for _, i := range accountInfos { + for _, j := range i.Fees { + if currency == j.Pairs { + if isMaker { + fee = j.MakerFees + } else { + fee = j.TakerFees + } + break + } + } + if fee > 0 { + break + } + } + return (fee / 100) * purchasePrice * amount, err +} diff --git a/exchanges/bitfinex/bitfinex_test.go b/exchanges/bitfinex/bitfinex_test.go index a499db1a..c225bb07 100644 --- a/exchanges/bitfinex/bitfinex_test.go +++ b/exchanges/bitfinex/bitfinex_test.go @@ -8,6 +8,8 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) // Please supply your own keys here to do better tests @@ -27,7 +29,8 @@ func TestSetup(t *testing.T) { t.Error("Test Failed - Bitfinex Setup() init error") } b.Setup(bfxConfig) - + b.APIKey = testAPIKey + b.APISecret = testAPISecret if !b.Enabled || b.AuthenticatedAPISupport || b.RESTPollingDelay != time.Duration(10) || b.Verbose || b.Websocket.IsEnabled() || len(b.BaseCurrencies) < 1 || len(b.AvailablePairs) < 1 || len(b.EnabledPairs) < 1 { @@ -617,3 +620,89 @@ func TestCloseMarginFunding(t *testing.T) { t.Error("Test Failed - CloseMarginFunding() error") } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + } +} + +func TestGetFee(t *testing.T) { + b.SetDefaults() + b.Verbose = true + TestSetup(t) + var feeBuilder = setFeeBuilder() + + if testAPIKey != "" || testAPISecret != "" { + // CryptocurrencyTradeFee Basic + if resp, err := b.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(2000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := b.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0.0004) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0004), resp) + t.Error(err) + } + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } +} diff --git a/exchanges/bitfinex/bitfinex_types.go b/exchanges/bitfinex/bitfinex_types.go index c6001858..8b449b8d 100644 --- a/exchanges/bitfinex/bitfinex_types.go +++ b/exchanges/bitfinex/bitfinex_types.go @@ -133,36 +133,22 @@ type AccountInfoFull struct { // AccountInfo general account information with fees type AccountInfo struct { - MakerFees string `json:"maker_fees"` - TakerFees string `json:"taker_fees"` + MakerFees float64 `json:"maker_fees,string"` + TakerFees float64 `json:"taker_fees,string"` Fees []AccountInfoFees `json:"fees"` Message string `json:"message"` } // AccountInfoFees general account information with fees type AccountInfoFees struct { - Pairs string `json:"pairs"` - MakerFees string `json:"maker_fees"` - TakerFees string `json:"taker_fees"` + Pairs string `json:"pairs"` + MakerFees float64 `json:"maker_fees,string"` + TakerFees float64 `json:"taker_fees,string"` } // AccountFees stores withdrawal account fee data from Bitfinex type AccountFees struct { - Withdraw struct { - BTC float64 `json:"BTC,string"` - LTC float64 `json:"LTC,string"` - ETH float64 `json:"ETH,string"` - ETC float64 `json:"ETC,string"` - ZEC float64 `json:"ZEC,string"` - XMR float64 `json:"XMR,string"` - DSH float64 `json:"DSH,string"` - XRP float64 `json:"XRP,string"` - IOT float64 `json:"IOT"` - EOS float64 `json:"EOS,string"` - SAN float64 `json:"SAN,string"` - OMG float64 `json:"OMG,string"` - BCH float64 `json:"BCH,string"` - } `json:"withdraw"` + Withdraw map[string]interface{} `json:"withdraw"` } // AccountSummary holds account summary data diff --git a/exchanges/bitfinex/bitfinex_wrapper.go b/exchanges/bitfinex/bitfinex_wrapper.go index 38541954..6ed3426c 100644 --- a/exchanges/bitfinex/bitfinex_wrapper.go +++ b/exchanges/bitfinex/bitfinex_wrapper.go @@ -226,3 +226,8 @@ func (b *Bitfinex) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Cu func (b *Bitfinex) GetWebsocket() (*exchange.Websocket, error) { return b.Websocket, nil } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (b *Bitfinex) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return b.GetFee(feeBuilder) +} diff --git a/exchanges/bitflyer/bitflyer.go b/exchanges/bitflyer/bitflyer.go index 039422c8..645ddd7d 100644 --- a/exchanges/bitflyer/bitflyer.go +++ b/exchanges/bitflyer/bitflyer.go @@ -10,6 +10,7 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" exchange "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -385,3 +386,55 @@ func (b *Bitflyer) SendAuthHTTPRequest(path string, params url.Values, result in headers["ACCESS-KEY"] = b.APIKey headers["ACCESS-TIMESTAMP"] = strconv.FormatInt(int64(time.Now().UnixNano()), 10) } + +// GetFee returns an estimate of fee based on type of transaction +// TODO: Figure out the weird fee structure. Do we use Bitcoin Easy Exchange,Lightning Spot,Bitcoin Market,Lightning FX/Futures ??? +func (b *Bitflyer) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount) + case exchange.InternationalBankDepositFee: + fee = getDepositFee(feeBuilder.BankTransactionType, feeBuilder.CurrencyItem, feeBuilder.Amount) + case exchange.InternationalBankWithdrawalFee: + fee = getWithdrawalFee(feeBuilder.BankTransactionType, feeBuilder.CurrencyItem, feeBuilder.Amount) + } + if fee < 0 { + fee = 0 + } + return fee, nil +} + +// getDepositFee returns fee when performing a trade +func calculateTradingFee(purchasePrice float64, amount float64) float64 { + fee := 0.0015 + // bitflyer has fee tiers, but does not disclose them via API, so the largest has to be assumed + return fee * amount * purchasePrice +} + +func getDepositFee(bankTransactionType exchange.InternationalBankTransactionType, currency string, amount float64) (fee float64) { + switch bankTransactionType { + case exchange.WireTransfer: + switch currency { + case symbol.JPY: + fee = 324 + } + } + return fee +} + +func getWithdrawalFee(bankTransactionType exchange.InternationalBankTransactionType, currency string, amount float64) (fee float64) { + switch bankTransactionType { + case exchange.WireTransfer: + switch currency { + case symbol.JPY: + if amount < 30000 { + fee = 540 + } else { + fee = 756 + } + } + } + return fee +} diff --git a/exchanges/bitflyer/bitflyer_test.go b/exchanges/bitflyer/bitflyer_test.go index 20f055bc..6e42f5f5 100644 --- a/exchanges/bitflyer/bitflyer_test.go +++ b/exchanges/bitflyer/bitflyer_test.go @@ -4,6 +4,9 @@ import ( "log" "testing" + "github.com/thrasher-/gocryptotrader/currency/symbol" + "github.com/thrasher-/gocryptotrader/exchanges" + "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/currency/pair" ) @@ -118,33 +121,6 @@ func TestGetExchangeStatus(t *testing.T) { } } -// func TestGetChats(t *testing.T) { -// t.Parallel() -// time := time.Now().Format(time.RFC3339) -// _, err := b.GetChats(time) -// if err != nil { -// t.Error("test failed - Bitflyer - GetChats() error:", err) -// } -// } - -// func TestUpdateTicker(t *testing.T) { -// t.Parallel() -// p := pair.NewCurrencyPairFromString("BTC_JPY") -// _, err := b.UpdateTicker(p, "SPOT") -// if err != nil { -// t.Error("test failed - Bitflyer - UpdateTicker() error:", err) -// } -// } -// -// func TestUpdateOrderbook(t *testing.T) { -// t.Parallel() -// p := pair.NewCurrencyPairFromString("BTC_JPY") -// _, err := b.UpdateOrderbook(p, "SPOT") -// if err != nil { -// t.Error("test failed - Bitflyer - UpdateOrderbook() error:", err) -// } -// } - func TestCheckFXString(t *testing.T) { t.Parallel() p := pair.NewCurrencyPairDelimiter("FXBTC_JPY", "_") @@ -171,3 +147,91 @@ func TestGetTickerPrice(t *testing.T) { t.Error("test failed - Bitflyer - GetTickerPrice() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.JPY, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + b.SetDefaults() + TestSetup(t) + var feeBuilder = setFeeBuilder() + + if testAPIKey != "" || testAPISecret != "" { + // CryptocurrencyTradeFee Basic + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := b.GetFee(feeBuilder); resp != float64(0.1) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.1), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.JPY + if resp, err := b.GetFee(feeBuilder); resp != float64(324) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(324), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.JPY + if resp, err := b.GetFee(feeBuilder); resp != float64(540) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(540), resp) + t.Error(err) + } +} diff --git a/exchanges/bithumb/bithumb.go b/exchanges/bithumb/bithumb.go index d0ea8c90..d73cbdb2 100644 --- a/exchanges/bithumb/bithumb.go +++ b/exchanges/bithumb/bithumb.go @@ -11,6 +11,7 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" exchange "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -564,3 +565,67 @@ func (b *Bithumb) SendAuthenticatedHTTPRequest(path string, params url.Values, r return b.SendPayload("POST", b.APIUrl+path, headers, bytes.NewBufferString(payload), result, true, b.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (b *Bithumb) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount) + case exchange.CyptocurrencyDepositFee: + fee = getDepositFee(feeBuilder.FirstCurrency, feeBuilder.Amount) + case exchange.CryptocurrencyWithdrawalFee: + fee = getWithdrawalFee(feeBuilder.FirstCurrency) + } + if fee < 0 { + fee = 0 + } + return fee, nil +} + +// getDepositFee returns fee when performing a trade +func calculateTradingFee(purchasePrice float64, amount float64) float64 { + fee := 0.0015 + + return fee * amount * purchasePrice +} + +// getDepositFee returns fee on a currency when depositing small amounts to bithumb +func getDepositFee(currency string, amount float64) float64 { + var fee float64 + + switch currency { + case symbol.BTC: + if amount <= 0.005 { + fee = 0.001 + } + case symbol.LTC: + if amount <= 0.3 { + fee = 0.01 + } + case symbol.DASH: + if amount <= 0.04 { + fee = 0.01 + } + case symbol.BCH: + if amount <= 0.03 { + fee = 0.001 + } + case symbol.ZEC: + if amount <= 0.02 { + fee = 0.001 + } + case symbol.BTG: + if amount <= 0.15 { + fee = 0.001 + } + } + + return fee +} + +// getWithdrawalFee returns fee on a currency when withdrawing out of bithumb +func getWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} diff --git a/exchanges/bithumb/bithumb_test.go b/exchanges/bithumb/bithumb_test.go index 9e9e3bcb..0dfbfcd1 100644 --- a/exchanges/bithumb/bithumb_test.go +++ b/exchanges/bithumb/bithumb_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) // Please supply your own keys here for due diligence testing @@ -185,34 +187,86 @@ func TestMarketSellOrder(t *testing.T) { } } -// func TestRun(t *testing.T) { -// t.Parallel() -// b.Run() -// } -// -// func TestUpdateTicker(t *testing.T) { -// t.Parallel() -// pair := b.GetEnabledCurrencies()[0] -// _, err := b.UpdateTicker(pair, b.AssetTypes[0]) -// if err != nil { -// t.Error("test failed - Bithumb UpdateTicker() error", err) -// } -// } -// -// func TestGetTickerPrice(t *testing.T) { -// t.Parallel() -// pair := b.GetEnabledCurrencies()[0] -// _, err := b.GetTickerPrice(pair, b.AssetTypes[0]) -// if err != nil { -// t.Error("test failed - Bithumb GetTickerPrice() error", err) -// } -// } -// -// func TestGetOrderbookEx(t *testing.T) { -// t.Parallel() -// pair := b.GetEnabledCurrencies()[0] -// _, err := b.GetOrderbookEx(pair, b.AssetTypes[0]) -// if err != nil { -// t.Error("test failed - Bithumb GetOrderbookEx() error", err) -// } -// } +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + } +} + +func TestGetFee(t *testing.T) { + b.SetDefaults() + TestSetup(t) + var feeBuilder = setFeeBuilder() + + // CryptocurrencyTradeFee Basic + if resp, err := b.GetFee(feeBuilder); resp != float64(0.0015) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0015), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(1500) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(1500), resp) + t.Error(err) + } + + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := b.GetFee(feeBuilder); resp != float64(0.0015) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0015), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/bithumb/bithumb_types.go b/exchanges/bithumb/bithumb_types.go index 653c120c..110d8f00 100644 --- a/exchanges/bithumb/bithumb_types.go +++ b/exchanges/bithumb/bithumb_types.go @@ -1,5 +1,7 @@ package bithumb +import "github.com/thrasher-/gocryptotrader/currency/symbol" + // Ticker holds ticker data type Ticker struct { OpeningPrice float64 `json:"opening_price,string"` @@ -227,3 +229,52 @@ type MarketSell struct { } `json:"data"` Message string `json:"message"` } + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change +var WithdrawalFees = map[string]float64{ + symbol.KRW: 1000, + symbol.BTC: 0.001, + symbol.ETH: 0.01, + symbol.DASH: 0.01, + symbol.LTC: 0.01, + symbol.ETC: 0.01, + symbol.XRP: 1, + symbol.BCH: 0.001, + symbol.XMR: 0.05, + symbol.ZEC: 0.001, + symbol.QTUM: 0.05, + symbol.BTG: 0.001, + symbol.ICX: 1, + symbol.TRX: 5, + symbol.ELF: 5, + symbol.MITH: 5, + symbol.MCO: 0.5, + symbol.OMG: 0.4, + symbol.KNC: 3, + symbol.GNT: 12, + symbol.HSR: 0.2, + symbol.ZIL: 30, + symbol.ETHOS: 2, + symbol.PAY: 2.4, + symbol.WAX: 5, + symbol.POWR: 5, + symbol.LRC: 10, + symbol.GTO: 15, + symbol.STEEM: 0.01, + symbol.STRAT: 0.2, + symbol.PPT: 0.5, + symbol.CTXC: 4, + symbol.CMT: 20, + symbol.THETA: 24, + symbol.WTC: 0.7, + symbol.ITC: 5, + symbol.TRUE: 4, + symbol.ABT: 5, + symbol.RNT: 20, + symbol.PLY: 20, + symbol.WAVES: 0.01, + symbol.LINK: 10, + symbol.ENJ: 35, + symbol.PST: 30, +} diff --git a/exchanges/bithumb/bithumb_wrapper.go b/exchanges/bithumb/bithumb_wrapper.go index 8b801c71..76b75d74 100644 --- a/exchanges/bithumb/bithumb_wrapper.go +++ b/exchanges/bithumb/bithumb_wrapper.go @@ -193,3 +193,8 @@ func (b *Bithumb) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Cur func (b *Bithumb) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (b *Bithumb) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return b.GetFee(feeBuilder) +} diff --git a/exchanges/bitmex/bitmex.go b/exchanges/bitmex/bitmex.go index 104aa47b..d778630f 100644 --- a/exchanges/bitmex/bitmex.go +++ b/exchanges/bitmex/bitmex.go @@ -912,3 +912,29 @@ func (b *Bitmex) CaptureError(resp, reType interface{}) error { } return nil } + +// GetFee returns an estimate of fee based on type of transaction +func (b *Bitmex) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + var err error + + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + } + if fee < 0 { + fee = 0 + } + return fee, err +} + +// calculateTradingFee returns the fee for trading any currency on Bittrex +func calculateTradingFee(purchasePrice float64, amount float64, isMaker bool) float64 { + var fee = 0.000750 + + if isMaker { + fee -= 0.000250 + } + + return fee * purchasePrice * amount +} diff --git a/exchanges/bitmex/bitmex_test.go b/exchanges/bitmex/bitmex_test.go index d0ac7dd7..d1eecf1b 100644 --- a/exchanges/bitmex/bitmex_test.go +++ b/exchanges/bitmex/bitmex_test.go @@ -6,6 +6,8 @@ import ( "time" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) // Please supply your own keys here for due diligence testing @@ -363,3 +365,87 @@ func TestGetPreviousTrades(t *testing.T) { t.Error("test failed - GetPreviousTrades() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + } +} + +func TestGetFee(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var feeBuilder = setFeeBuilder() + + // CryptocurrencyTradeFee Basic + if resp, err := b.GetFee(feeBuilder); resp != float64(0.00075) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.00075), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(750) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(750), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := b.GetFee(feeBuilder); resp != float64(0.0005) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0005), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/bitmex/bitmex_wrapper.go b/exchanges/bitmex/bitmex_wrapper.go index 2d306e36..f4cd8d5e 100644 --- a/exchanges/bitmex/bitmex_wrapper.go +++ b/exchanges/bitmex/bitmex_wrapper.go @@ -197,3 +197,8 @@ func (b *Bitmex) WithdrawExchangeFiatFundsToInternationalBank(currency pair.Curr func (b *Bitmex) GetWebsocket() (*exchange.Websocket, error) { return b.Websocket, nil } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (b *Bitmex) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return b.GetFee(feeBuilder) +} diff --git a/exchanges/bitstamp/bitstamp.go b/exchanges/bitstamp/bitstamp.go index b96c4cac..969aadf5 100644 --- a/exchanges/bitstamp/bitstamp.go +++ b/exchanges/bitstamp/bitstamp.go @@ -12,6 +12,7 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -129,22 +130,74 @@ func (b *Bitstamp) Setup(exch config.ExchangeConfig) { } } -// GetFee returns fee on a currency pair -func (b *Bitstamp) GetFee(currencyPair string) float64 { - switch currencyPair { - case "BTCUSD": - return b.Balance.BTCUSDFee - case "BTCEUR": - return b.Balance.BTCEURFee - case "XRPEUR": - return b.Balance.XRPEURFee - case "XRPUSD": - return b.Balance.XRPUSDFee - case "EURUSD": - return b.Balance.EURUSDFee - default: - return 0 +// GetFee returns an estimate of fee based on type of transaction +func (b *Bitstamp) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + var err error + + b.Balance, err = b.GetBalance() + if err != nil { + return 0, err + } + fee = b.CalculateTradingFee(feeBuilder.FirstCurrency+feeBuilder.SecondCurrency, feeBuilder.PurchasePrice, feeBuilder.Amount) + case exchange.CyptocurrencyDepositFee: + fee = 0 + case exchange.InternationalBankDepositFee: + fee = getInternationalBankDepositFee(feeBuilder.Amount) + case exchange.InternationalBankWithdrawalFee: + fee = getInternationalBankWithdrawalFee(feeBuilder.Amount) } + if fee < 0 { + fee = 0 + } + return fee, nil +} + +// getInternationalBankWithdrawalFee returns international withdrawal fee +func getInternationalBankWithdrawalFee(amount float64) float64 { + fee := amount * 0.0009 + + if fee < 15 { + return 15 + } + return fee +} + +// getInternationalBankDepositFee returns international deposit fee +func getInternationalBankDepositFee(amount float64) float64 { + fee := amount * 0.0005 + + if fee < 7.5 { + return 7.5 + } + if fee > 300 { + return 300 + } + return fee +} + +// CalculateTradingFee returns fee on a currency pair +func (b *Bitstamp) CalculateTradingFee(currency string, purchasePrice float64, amount float64) float64 { + var fee float64 + + switch currency { + case symbol.BTC + symbol.USD: + fee = b.Balance.BTCUSDFee + case symbol.BTC + symbol.EUR: + fee = b.Balance.BTCEURFee + case symbol.XRP + symbol.EUR: + fee = b.Balance.XRPEURFee + case symbol.XRP + symbol.USD: + fee = b.Balance.XRPUSDFee + case symbol.EUR + symbol.USD: + fee = b.Balance.EURUSDFee + default: + fee = 0 + } + return fee * purchasePrice * amount } // GetTicker returns ticker information diff --git a/exchanges/bitstamp/bitstamp_test.go b/exchanges/bitstamp/bitstamp_test.go index 3bb78466..b269ca0b 100644 --- a/exchanges/bitstamp/bitstamp_test.go +++ b/exchanges/bitstamp/bitstamp_test.go @@ -5,6 +5,9 @@ import ( "testing" "time" + "github.com/thrasher-/gocryptotrader/currency/symbol" + "github.com/thrasher-/gocryptotrader/exchanges" + "github.com/thrasher-/gocryptotrader/config" ) @@ -53,23 +56,118 @@ func TestSetup(t *testing.T) { } } +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + } +} + func TestGetFee(t *testing.T) { - t.Parallel() - if resp := b.GetFee("BTCUSD"); resp != 0 { + b.SetDefaults() + TestSetup(t) + + var feeBuilder = setFeeBuilder() + + // CryptocurrencyTradeFee Basic + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(7.5) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(7.5), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(15) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(15), resp) + t.Error(err) + } +} + +func TestCalculateTradingFee(t *testing.T) { + b.SetDefaults() + TestSetup(t) + b.Balance = Balances{} + b.Balance.BTCUSDFee = 1 + b.Balance.BTCEURFee = 0 + + if resp := b.CalculateTradingFee(symbol.BTC+symbol.USD, 0, 0); resp != 0 { t.Error("Test Failed - GetFee() error") } - if resp := b.GetFee("bla"); resp != 0 { + if resp := b.CalculateTradingFee(symbol.BTC+symbol.USD, 2, 2); resp != float64(4) { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(4), resp) + } + if resp := b.CalculateTradingFee(symbol.BTC+symbol.EUR, 2, 2); resp != float64(0) { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + } + if resp := b.CalculateTradingFee("bla", 0, 0); resp != 0 { t.Error("Test Failed - GetFee() error") } } func TestGetTicker(t *testing.T) { t.Parallel() - _, err := b.GetTicker("BTCUSD", false) + _, err := b.GetTicker(symbol.BTC+symbol.USD, false) if err != nil { t.Error("Test Failed - GetTicker() error", err) } - _, err = b.GetTicker("BTCUSD", true) + _, err = b.GetTicker(symbol.BTC+symbol.USD, true) if err != nil { t.Error("Test Failed - GetTicker() error", err) } @@ -77,7 +175,7 @@ func TestGetTicker(t *testing.T) { func TestGetOrderbook(t *testing.T) { t.Parallel() - _, err := b.GetOrderbook("BTCUSD") + _, err := b.GetOrderbook(symbol.BTC + symbol.USD) if err != nil { t.Error("Test Failed - GetOrderbook() error", err) } @@ -96,7 +194,7 @@ func TestGetTransactions(t *testing.T) { value := url.Values{} value.Set("time", "hour") - _, err := b.GetTransactions("BTCUSD", value) + _, err := b.GetTransactions(symbol.BTC+symbol.USD, value) if err != nil { t.Error("Test Failed - GetTransactions() error", err) } diff --git a/exchanges/bitstamp/bitstamp_wrapper.go b/exchanges/bitstamp/bitstamp_wrapper.go index 4953f241..aca4728e 100644 --- a/exchanges/bitstamp/bitstamp_wrapper.go +++ b/exchanges/bitstamp/bitstamp_wrapper.go @@ -77,6 +77,12 @@ func (b *Bitstamp) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker return tick, nil } +// GetFeeByType returns an estimate of fee based on type of transaction +func (b *Bitstamp) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return b.GetFee(feeBuilder) + +} + // GetOrderbookEx returns the orderbook for a currency pair func (b *Bitstamp) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) { ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType) diff --git a/exchanges/bittrex/bittrex.go b/exchanges/bittrex/bittrex.go index 9ec38ad0..fab87dd3 100644 --- a/exchanges/bittrex/bittrex.go +++ b/exchanges/bittrex/bittrex.go @@ -511,3 +511,43 @@ func (b *Bittrex) SendAuthenticatedHTTPRequest(path string, values url.Values, r return b.SendPayload("GET", rawQuery, headers, nil, result, true, b.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (b *Bittrex) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + var err error + + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount) + case exchange.CryptocurrencyWithdrawalFee: + fee, err = b.GetWithdrawalFee(feeBuilder.FirstCurrency) + } + if fee < 0 { + fee = 0 + } + return fee, err +} + +// GetWithdrawalFee returns the fee for withdrawing from the exchange +func (b *Bittrex) GetWithdrawalFee(currency string) (float64, error) { + var fee float64 + + currencies, err := b.GetCurrencies() + if err != nil { + return 0, err + } + for _, result := range currencies.Result { + if result.Currency == currency { + fee = result.TxFee + } + } + return fee, nil +} + +// calculateTradingFee returns the fee for trading any currency on Bittrex +func calculateTradingFee(purchasePrice float64, amount float64) float64 { + var fee = 0.0025 + + return fee * purchasePrice * amount +} diff --git a/exchanges/bittrex/bittrex_test.go b/exchanges/bittrex/bittrex_test.go index 6501a0ae..59dec0a2 100644 --- a/exchanges/bittrex/bittrex_test.go +++ b/exchanges/bittrex/bittrex_test.go @@ -5,6 +5,8 @@ import ( "time" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) // Please supply you own test keys here to run better tests. @@ -229,3 +231,87 @@ func TestGetDepositHistory(t *testing.T) { t.Error("Test Failed - Bittrex - GetDepositHistory() error") } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + } +} + +func TestGetFee(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var feeBuilder = setFeeBuilder() + + // CryptocurrencyTradeFee Basic + if resp, err := b.GetFee(feeBuilder); resp != float64(0.0025) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0025), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(2500) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2500), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := b.GetFee(feeBuilder); resp != float64(0.0025) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0025), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0.0005) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0005), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.HKD + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/bittrex/bittrex_wrapper.go b/exchanges/bittrex/bittrex_wrapper.go index cee54302..45429031 100644 --- a/exchanges/bittrex/bittrex_wrapper.go +++ b/exchanges/bittrex/bittrex_wrapper.go @@ -222,3 +222,9 @@ func (b *Bittrex) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Cur func (b *Bittrex) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (b *Bittrex) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return b.GetFee(feeBuilder) + +} diff --git a/exchanges/btcc/btcc.go b/exchanges/btcc/btcc.go index be55dbbc..7ca9e355 100644 --- a/exchanges/btcc/btcc.go +++ b/exchanges/btcc/btcc.go @@ -93,7 +93,31 @@ func (b *BTCC) Setup(exch config.ExchangeConfig) { } } -// GetFee returns the fees associated with transactions -func (b *BTCC) GetFee() float64 { - return b.Fee + + +// GetFee returns an estimate of fee based on type of transaction +func (b *BTCC) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + + switch feeBuilder.FeeType { + case exchange.CryptocurrencyWithdrawalFee: + fee = getCryptocurrencyWithdrawalFee(feeBuilder.FirstCurrency) + case exchange.InternationalBankWithdrawalFee: + fee = getInternationalBankWithdrawalFee(feeBuilder.CurrencyItem, feeBuilder.Amount) + } + if fee < 0 { + fee = 0 + } + return fee, nil +} + +func getCryptocurrencyWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} + +func getInternationalBankWithdrawalFee(currency string, amount float64) float64 { + var fee float64 + + fee = WithdrawalFees[currency] * amount + return fee } diff --git a/exchanges/btcc/btcc_test.go b/exchanges/btcc/btcc_test.go index 9c5886e9..75451c52 100644 --- a/exchanges/btcc/btcc_test.go +++ b/exchanges/btcc/btcc_test.go @@ -5,6 +5,8 @@ import ( "time" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) // Please supply your own APIkeys here to do better tests @@ -36,12 +38,6 @@ func TestSetup(t *testing.T) { } } -func TestGetFee(t *testing.T) { - if b.GetFee() != 0 { - t.Error("Test failed - GetFee() error") - } -} - // func TestGetTicker(t *testing.T) { // t.Skip() // _, err := b.GetTicker("BTCUSD") @@ -77,3 +73,86 @@ func TestGetFee(t *testing.T) { // t.Error("Test failed - GetAccountInfo() error", err) // } // } +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + } +} + +func TestGetFee(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var feeBuilder = setFeeBuilder() + + // CryptocurrencyTradeFee Basic + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := b.GetFee(feeBuilder); resp != float64(0.005) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.005), resp) + t.Error(err) + } +} diff --git a/exchanges/btcc/btcc_types.go b/exchanges/btcc/btcc_types.go index 1feffd6b..fc140236 100644 --- a/exchanges/btcc/btcc_types.go +++ b/exchanges/btcc/btcc_types.go @@ -1,6 +1,7 @@ package btcc import "encoding/json" +import "github.com/thrasher-/gocryptotrader/currency/symbol" // WsAllTickerData defines multiple ticker data type WsAllTickerData []WsTicker @@ -80,3 +81,15 @@ type WsTicker struct { ExecutionLimitUp float64 `json:"ExecutionLimitUp"` MsgType string `json:"MsgType"` } + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change +var WithdrawalFees = map[string]float64{ + symbol.USD: 0.005, + symbol.USDT: 10, + symbol.BTC: 0.001, + symbol.ETH: 0.01, + symbol.BCH: 0.0001, + symbol.LTC: 0.001, + symbol.DASH: 0.002, +} diff --git a/exchanges/btcc/btcc_wrapper.go b/exchanges/btcc/btcc_wrapper.go index ffcb5bcc..272a900f 100644 --- a/exchanges/btcc/btcc_wrapper.go +++ b/exchanges/btcc/btcc_wrapper.go @@ -204,3 +204,8 @@ func (b *BTCC) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curren func (b *BTCC) GetWebsocket() (*exchange.Websocket, error) { return b.Websocket, nil } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (b *BTCC) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return b.GetFee(feeBuilder) +} diff --git a/exchanges/btcmarkets/btcmarkets.go b/exchanges/btcmarkets/btcmarkets.go index a36d3f27..4d02a6ad 100644 --- a/exchanges/btcmarkets/btcmarkets.go +++ b/exchanges/btcmarkets/btcmarkets.go @@ -10,6 +10,7 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -19,6 +20,7 @@ const ( btcMarketsAPIURL = "https://api.btcmarkets.net" btcMarketsAPIVersion = "0" btcMarketsAccountBalance = "/account/balance" + btcMarketsTradingFee = "/account/%s/%s/tradingfee" btcMarketsOrderCreate = "/order/create" btcMarketsOrderCancel = "/order/cancel" btcMarketsOrderHistory = "/order/history" @@ -110,11 +112,6 @@ func (b *BTCMarkets) Setup(exch config.ExchangeConfig) { } } -// GetFee returns the BTCMarkets fee on transactions -func (b *BTCMarkets) GetFee() float64 { - return b.Fee -} - // GetMarkets returns the BTCMarkets instruments func (b *BTCMarkets) GetMarkets() ([]Market, error) { type marketsResp struct { @@ -347,6 +344,13 @@ func (b *BTCMarkets) GetAccountBalance() ([]AccountBalance, error) { return balance, nil } +// GetTradingFee returns the account's trading fee for a currency pair +func (b *BTCMarkets) GetTradingFee(firstPair, secondPair string) (TradingFee, error) { + var tradingFee TradingFee + path := fmt.Sprintf(btcMarketsTradingFee, firstPair, secondPair) + return tradingFee, b.SendAuthenticatedRequest("GET", path, nil, &tradingFee) +} + // WithdrawCrypto withdraws cryptocurrency into a designated address func (b *BTCMarkets) WithdrawCrypto(amount float64, currency, address string) (string, error) { newAmount := int64(amount * float64(common.SatoshisPerBTC)) @@ -442,3 +446,44 @@ func (b *BTCMarkets) SendAuthenticatedRequest(reqType, path string, data interfa return b.SendPayload(reqType, b.APIUrl+path, headers, bytes.NewBuffer(payload), result, true, b.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (b *BTCMarkets) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + tradingFee, err := b.GetTradingFee(feeBuilder.FirstCurrency, feeBuilder.SecondCurrency) + if err != nil { + return 0, err + } + fee = calculateTradingFee(feeBuilder.FirstCurrency+feeBuilder.Delimiter+feeBuilder.SecondCurrency, tradingFee, feeBuilder.PurchasePrice, feeBuilder.Amount) + case exchange.CryptocurrencyWithdrawalFee: + fee = getCryptocurrencyWithdrawalFee(feeBuilder.FirstCurrency) + case exchange.InternationalBankWithdrawalFee: + fee = getInternationalBankWithdrawalFee(feeBuilder.CurrencyItem, feeBuilder.Amount) + } + if fee < 0 { + fee = 0 + } + return fee, nil +} + +func calculateTradingFee(curr string, tradingFee TradingFee, purchasePrice, amount float64) (fee float64) { + fee = tradingFee.TradingFeeRate / 100000000 + + return fee * amount * purchasePrice +} + +func getCryptocurrencyWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} + +func getInternationalBankWithdrawalFee(currency string, amount float64) float64 { + var fee float64 + + if currency == symbol.AUD { + fee = 0 + } + return fee +} diff --git a/exchanges/btcmarkets/btcmarkets_test.go b/exchanges/btcmarkets/btcmarkets_test.go index 3f884b37..626f6143 100644 --- a/exchanges/btcmarkets/btcmarkets_test.go +++ b/exchanges/btcmarkets/btcmarkets_test.go @@ -6,10 +6,11 @@ import ( "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/currency/pair" + "github.com/thrasher-/gocryptotrader/currency/symbol" exchange "github.com/thrasher-/gocryptotrader/exchanges" ) -var bm BTCMarkets +var b BTCMarkets // Please supply your own keys here to do better tests const ( @@ -18,7 +19,7 @@ const ( ) func TestSetDefaults(t *testing.T) { - bm.SetDefaults() + b.SetDefaults() } func TestSetup(t *testing.T) { @@ -35,19 +36,12 @@ func TestSetup(t *testing.T) { bConfig.AuthenticatedAPISupport = true } - bm.Setup(bConfig) -} - -func TestGetFee(t *testing.T) { - t.Parallel() - if fee := bm.GetFee(); fee == 0 { - t.Error("Test failed - GetFee() error") - } + b.Setup(bConfig) } func TestGetMarkets(t *testing.T) { t.Parallel() - _, err := bm.GetMarkets() + _, err := b.GetMarkets() if err != nil { t.Error("Test failed - GetMarkets() error", err) } @@ -55,7 +49,7 @@ func TestGetMarkets(t *testing.T) { func TestGetTicker(t *testing.T) { t.Parallel() - _, err := bm.GetTicker("BTC", "AUD") + _, err := b.GetTicker("BTC", "AUD") if err != nil { t.Error("Test failed - GetTicker() error", err) } @@ -63,7 +57,7 @@ func TestGetTicker(t *testing.T) { func TestGetOrderbook(t *testing.T) { t.Parallel() - _, err := bm.GetOrderbook("BTC", "AUD") + _, err := b.GetOrderbook("BTC", "AUD") if err != nil { t.Error("Test failed - GetOrderbook() error", err) } @@ -71,14 +65,14 @@ func TestGetOrderbook(t *testing.T) { func TestGetTrades(t *testing.T) { t.Parallel() - _, err := bm.GetTrades("BTC", "AUD", nil) + _, err := b.GetTrades("BTC", "AUD", nil) if err != nil { t.Error("Test failed - GetTrades() error", err) } val := url.Values{} val.Set("since", "0") - _, err = bm.GetTrades("BTC", "AUD", val) + _, err = b.GetTrades("BTC", "AUD", val) if err != nil { t.Error("Test failed - GetTrades() error", err) } @@ -86,7 +80,7 @@ func TestGetTrades(t *testing.T) { func TestNewOrder(t *testing.T) { t.Parallel() - _, err := bm.NewOrder("AUD", "BTC", 0, 0, "Bid", "limit", "testTest") + _, err := b.NewOrder("AUD", "BTC", 0, 0, "Bid", "limit", "testTest") if err == nil { t.Error("Test failed - NewOrder() error", err) } @@ -94,7 +88,7 @@ func TestNewOrder(t *testing.T) { func TestCancelOrder(t *testing.T) { t.Parallel() - _, err := bm.CancelOrder([]int64{1337}) + _, err := b.CancelOrder([]int64{1337}) if err == nil { t.Error("Test failed - CancelOrder() error", err) } @@ -102,11 +96,11 @@ func TestCancelOrder(t *testing.T) { func TestGetOrders(t *testing.T) { t.Parallel() - _, err := bm.GetOrders("AUD", "BTC", 10, 0, false) + _, err := b.GetOrders("AUD", "BTC", 10, 0, false) if err == nil { t.Error("Test failed - GetOrders() error", err) } - _, err = bm.GetOrders("AUD", "BTC", 10, 0, true) + _, err = b.GetOrders("AUD", "BTC", 10, 0, true) if err == nil { t.Error("Test failed - GetOrders() error", err) } @@ -114,7 +108,7 @@ func TestGetOrders(t *testing.T) { func TestGetOrderDetail(t *testing.T) { t.Parallel() - _, err := bm.GetOrderDetail([]int64{1337}) + _, err := b.GetOrderDetail([]int64{1337}) if err == nil { t.Error("Test failed - GetOrderDetail() error", err) } @@ -122,7 +116,7 @@ func TestGetOrderDetail(t *testing.T) { func TestGetAccountBalance(t *testing.T) { t.Parallel() - _, err := bm.GetAccountBalance() + _, err := b.GetAccountBalance() if err == nil { t.Error("Test failed - GetAccountBalance() error", err) } @@ -130,7 +124,7 @@ func TestGetAccountBalance(t *testing.T) { func TestWithdrawCrypto(t *testing.T) { t.Parallel() - _, err := bm.WithdrawCrypto(0, "BTC", "LOLOLOL") + _, err := b.WithdrawCrypto(0, "BTC", "LOLOLOL") if err == nil { t.Error("Test failed - WithdrawCrypto() error", err) } @@ -138,21 +132,21 @@ func TestWithdrawCrypto(t *testing.T) { func TestWithdrawAUD(t *testing.T) { t.Parallel() - _, err := bm.WithdrawAUD("BLA", "1337", "blawest", "1336", 10000000) + _, err := b.WithdrawAUD("BLA", "1337", "blawest", "1336", 10000000) if err == nil { t.Error("Test failed - WithdrawAUD() error", err) } } func TestGetExchangeAccountInfo(t *testing.T) { - _, err := bm.GetExchangeAccountInfo() + _, err := b.GetExchangeAccountInfo() if err == nil { t.Error("Test failed - GetExchangeAccountInfo() error", err) } } func TestGetExchangeFundTransferHistory(t *testing.T) { - _, err := bm.GetExchangeFundTransferHistory() + _, err := b.GetExchangeFundTransferHistory() if err == nil { t.Error("Test failed - GetExchangeAccountInfo() error", err) } @@ -160,50 +154,145 @@ func TestGetExchangeFundTransferHistory(t *testing.T) { func TestSubmitExchangeOrder(t *testing.T) { p := pair.NewCurrencyPair("LTC", "AUD") - _, err := bm.SubmitExchangeOrder(p, exchange.OrderSideSell(), exchange.OrderTypeMarket(), 0, 0.0, "testID001") + _, err := b.SubmitExchangeOrder(p, exchange.OrderSideSell(), exchange.OrderTypeMarket(), 0, 0.0, "testID001") if err == nil { t.Error("Test failed - SubmitExchangeOrder() error", err) } } func TestModifyExchangeOrder(t *testing.T) { - _, err := bm.ModifyExchangeOrder(1337, exchange.ModifyOrder{}) + _, err := b.ModifyExchangeOrder(1337, exchange.ModifyOrder{}) if err == nil { t.Error("Test failed - ModifyExchangeOrder() error", err) } } func TestCancelExchangeOrder(t *testing.T) { - err := bm.CancelExchangeOrder(1337) + err := b.CancelExchangeOrder(1337) if err == nil { t.Error("Test failed - CancelExchangeOrder() error", err) } } func TestCancelAllExchangeOrders(t *testing.T) { - err := bm.CancelAllExchangeOrders() + err := b.CancelAllExchangeOrders() if err == nil { t.Error("Test failed - CancelAllExchangeOrders() error", err) } } func TestGetExchangeOrderInfo(t *testing.T) { - _, err := bm.GetExchangeOrderInfo(1337) + _, err := b.GetExchangeOrderInfo(1337) if err == nil { t.Error("Test failed - GetExchangeOrderInfo() error", err) } } func TestWithdrawCryptoExchangeFunds(t *testing.T) { - _, err := bm.WithdrawCryptoExchangeFunds("someaddress", "ltc", 0) + _, err := b.WithdrawCryptoExchangeFunds("someaddress", "ltc", 0) if err == nil { t.Error("Test failed - WithdrawExchangeFunds() error", err) } } func TestWithdrawFiatExchangeFunds(t *testing.T) { - _, err := bm.WithdrawFiatExchangeFunds("AUD", 0) + _, err := b.WithdrawFiatExchangeFunds("AUD", 0) if err == nil { t.Error("Test failed - WithdrawFiatExchangeFunds() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + } +} + +func TestGetFee(t *testing.T) { + b.SetDefaults() + TestSetup(t) + + var feeBuilder = setFeeBuilder() + + if apiKey != "" || apiSecret != "" { + // CryptocurrencyTradeFee Fiat + feeBuilder = setFeeBuilder() + feeBuilder.SecondCurrency = symbol.USD + if resp, err := b.GetFee(feeBuilder); resp != float64(0.00849999) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.00849999), resp) + } + + // CryptocurrencyTradeFee Basic + feeBuilder = setFeeBuilder() + if resp, err := b.GetFee(feeBuilder); resp != float64(0.0022) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0022), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(2200) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(22000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := b.GetFee(feeBuilder); resp != float64(0.0022) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0022), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.AUD + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.AUD + if resp, err := b.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/btcmarkets/btcmarkets_types.go b/exchanges/btcmarkets/btcmarkets_types.go index fe4ae0a0..f54a0445 100644 --- a/exchanges/btcmarkets/btcmarkets_types.go +++ b/exchanges/btcmarkets/btcmarkets_types.go @@ -1,5 +1,7 @@ package btcmarkets +import "github.com/thrasher-/gocryptotrader/currency/symbol" + // Response is the genralized response type type Response struct { Success bool `json:"success"` @@ -51,6 +53,15 @@ type Trade struct { Date int64 `json:"date"` } +// 30 day trade volume +type TradingFee struct { + Success bool `json:"success"` + ErrorCode int `json:"errorCode"` + ErrorMessage string `json:"errorMessage"` + TradingFeeRate float64 `json:"tradingfeerate"` + Volume30Day float64 `json:"volume30day"` +} + // OrderToGo holds order information to be sent to the exchange type OrderToGo struct { Currency string `json:"currency"` @@ -112,3 +123,17 @@ type WithdrawRequestAUD struct { BankName string `json:"bankName"` BSBNumber string `json:"bsbNumber"` } + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change +var WithdrawalFees = map[string]float64{ + symbol.AUD: 0, + symbol.BTC: 0.001, + symbol.ETH: 0.001, + symbol.ETC: 0.001, + symbol.LTC: 0.0001, + symbol.XRP: 0.15, + symbol.BCH: 0.0001, + symbol.OMG: 0.15, + symbol.POWR: 5, +} diff --git a/exchanges/btcmarkets/btcmarkets_wrapper.go b/exchanges/btcmarkets/btcmarkets_wrapper.go index 0339ffd4..5d842df2 100644 --- a/exchanges/btcmarkets/btcmarkets_wrapper.go +++ b/exchanges/btcmarkets/btcmarkets_wrapper.go @@ -255,3 +255,8 @@ func (b *BTCMarkets) WithdrawFiatExchangeFundsToInternationalBank(currency pair. func (b *BTCMarkets) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (b *BTCMarkets) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return b.GetFee(feeBuilder) +} diff --git a/exchanges/coinbasepro/coinbasepro.go b/exchanges/coinbasepro/coinbasepro.go index d1dee89b..69b5ce89 100644 --- a/exchanges/coinbasepro/coinbasepro.go +++ b/exchanges/coinbasepro/coinbasepro.go @@ -7,11 +7,13 @@ import ( "log" "net/url" "strconv" + "strings" "time" "github.com/gorilla/websocket" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -134,14 +136,6 @@ func (c *CoinbasePro) Setup(exch config.ExchangeConfig) { } } -// GetFee returns the current fee for the exchange -func (c *CoinbasePro) GetFee(maker bool) float64 { - if maker { - return c.MakerFee - } - return c.TakerFee -} - // GetProducts returns supported currency pairs on the exchange with specific // information about the pair func (c *CoinbasePro) GetProducts() ([]Product, error) { @@ -828,3 +822,70 @@ func (c *CoinbasePro) SendAuthenticatedHTTPRequest(method, path string, params m return c.SendPayload(method, c.APIUrl+path, headers, bytes.NewBuffer(payload), result, true, c.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (c *CoinbasePro) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + trailingVolume, err := c.GetTrailingVolume() + if err != nil { + return 0, err + } + fee = c.calculateTradingFee(trailingVolume, feeBuilder.FirstCurrency, feeBuilder.Delimiter, feeBuilder.SecondCurrency, feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + case exchange.InternationalBankWithdrawalFee: + fee = getInternationalBankWithdrawalFee(feeBuilder.CurrencyItem, feeBuilder.Amount) + case exchange.InternationalBankDepositFee: + fee = getInternationalBankDepositFee(feeBuilder.CurrencyItem, feeBuilder.Amount) + } + + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func (c *CoinbasePro) calculateTradingFee(trailingVolume []Volume, firstCurrency, delimiter, secondCurrency string, purchasePrice, amount float64, isMaker bool) float64 { + var fee float64 + for _, i := range trailingVolume { + if strings.EqualFold(i.ProductID, firstCurrency+delimiter+secondCurrency) { + if isMaker { + fee = 0 + } else if i.Volume <= 10000000 { + fee = 0.003 + } else if i.Volume > 10000000 && i.Volume <= 100000000 { + fee = 0.002 + } else if i.Volume > 100000000 { + fee = 0.001 + } + break + } + } + + return fee * amount * purchasePrice +} + +func getInternationalBankWithdrawalFee(currency string, amount float64) float64 { + var fee float64 + + if currency == symbol.USD { + fee = 25 + } else if currency == symbol.EUR { + fee = 0.15 + } + + return fee +} + +func getInternationalBankDepositFee(currency string, amount float64) float64 { + var fee float64 + + if currency == symbol.USD { + fee = 10 + } else if currency == symbol.EUR { + fee = 0.15 + } + + return fee +} diff --git a/exchanges/coinbasepro/coinbasepro_test.go b/exchanges/coinbasepro/coinbasepro_test.go index a109faa4..03b1817a 100644 --- a/exchanges/coinbasepro/coinbasepro_test.go +++ b/exchanges/coinbasepro/coinbasepro_test.go @@ -5,6 +5,8 @@ import ( "time" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var c CoinbasePro @@ -32,15 +34,6 @@ func TestSetup(t *testing.T) { c.Setup(gdxConfig) } -func TestGetFee(t *testing.T) { - if c.GetFee(false) == 0 { - t.Error("Test failed - GetFee() error") - } - if c.GetFee(true) != 0 { - t.Error("Test failed - GetFee() error") - } -} - func TestGetProducts(t *testing.T) { _, err := c.GetProducts() if err != nil { @@ -224,3 +217,176 @@ func TestAuthRequests(t *testing.T) { } } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + } +} + +func TestGetFee(t *testing.T) { + c.SetDefaults() + TestSetup(t) + + var feeBuilder = setFeeBuilder() + + if apiKey != "" || apiSecret != "" { + // CryptocurrencyTradeFee Basic + if resp, err := c.GetFee(feeBuilder); resp != float64(0.003) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.003), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := c.GetFee(feeBuilder); resp != float64(3000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(3000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := c.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.01), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := c.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := c.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := c.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.EUR + if resp, err := c.GetFee(feeBuilder); resp != float64(0.15) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := c.GetFee(feeBuilder); resp != float64(25) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} + +func TestCalculateTradingFee(t *testing.T) { + t.Parallel() + // uppercase + var volume = []Volume{ + Volume{ + ProductID: "BTC_USD", + Volume: 100, + }, + } + + if resp := c.calculateTradingFee(volume, "btc", "_", "usd", 1, 1, false); resp != float64(0.003) { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.003), resp) + } + + // lowercase + volume = []Volume{ + Volume{ + ProductID: "btc_usd", + Volume: 100, + }, + } + + if resp := c.calculateTradingFee(volume, "btc", "_", "usd", 1, 1, false); resp != float64(0.003) { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.003), resp) + } + + // mixedCase + volume = []Volume{ + Volume{ + ProductID: "btc_USD", + Volume: 100, + }, + } + + if resp := c.calculateTradingFee(volume, "btc", "_", "usd", 1, 1, false); resp != float64(0.003) { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.003), resp) + } + + // medium volume + volume = []Volume{ + Volume{ + ProductID: "btc_USD", + Volume: 10000001, + }, + } + + if resp := c.calculateTradingFee(volume, "btc", "_", "usd", 1, 1, false); resp != float64(0.002) { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + } + + // high volume + volume = []Volume{ + Volume{ + ProductID: "btc_USD", + Volume: 100000010000, + }, + } + + if resp := c.calculateTradingFee(volume, "btc", "_", "usd", 1, 1, false); resp != float64(0.001) { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + } + + // no match + volume = []Volume{ + Volume{ + ProductID: "btc_beeteesee", + Volume: 100000010000, + }, + } + + if resp := c.calculateTradingFee(volume, "btc", "_", "usd", 1, 1, false); resp != float64(0) { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + } + + // taker + volume = []Volume{ + Volume{ + ProductID: "btc_USD", + Volume: 100000010000, + }, + } + + if resp := c.calculateTradingFee(volume, "btc", "_", "usd", 1, 1, true); resp != float64(0) { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + } +} diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index e5a2790d..ddac22d2 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -191,3 +191,8 @@ func (c *CoinbasePro) WithdrawFiatExchangeFunds(cryptocurrency pair.CurrencyItem func (c *CoinbasePro) GetWebsocket() (*exchange.Websocket, error) { return c.Websocket, nil } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (c *CoinbasePro) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return c.GetFee(feeBuilder) +} diff --git a/exchanges/coinut/coinut.go b/exchanges/coinut/coinut.go index 2c0525e5..e072d98e 100644 --- a/exchanges/coinut/coinut.go +++ b/exchanges/coinut/coinut.go @@ -10,6 +10,8 @@ import ( "github.com/gorilla/websocket" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency" + "github.com/thrasher-/gocryptotrader/currency/symbol" "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -330,3 +332,82 @@ func (c *COINUT) SendHTTPRequest(apiRequest string, params map[string]interface{ return c.SendPayload("POST", c.APIUrl, headers, bytes.NewBuffer(payload), result, authenticated, c.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (c *COINUT) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = c.calculateTradingFee(feeBuilder.FirstCurrency, feeBuilder.SecondCurrency, feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + case exchange.InternationalBankWithdrawalFee: + fee = getInternationalBankWithdrawalFee(feeBuilder.CurrencyItem, feeBuilder.Amount) + case exchange.InternationalBankDepositFee: + fee = getInternationalBankDepositFee(feeBuilder.CurrencyItem, feeBuilder.Amount) + } + + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func (c *COINUT) calculateTradingFee(firstCurrency, secondCurrency string, purchasePrice, amount float64, isMaker bool) float64 { + var fee float64 + if isMaker { + fee = 0 + } else if currency.IsCryptocurrency(firstCurrency) && !currency.IsCryptocurrency(secondCurrency) || + !currency.IsCryptocurrency(firstCurrency) && currency.IsCryptocurrency(secondCurrency) { + fee = 0.002 + } else { + fee = 0.001 + } + + return fee * amount * purchasePrice +} + +func getInternationalBankWithdrawalFee(currency string, amount float64) float64 { + var fee float64 + + if currency == symbol.USD { + if amount*0.001 < 10 { + fee = 10 + } else { + fee = amount * 0.001 + } + } else if currency == symbol.CAD { + if amount*0.005 < 10 { + fee = 2 + } else { + fee = amount * 0.005 + } + } else if currency == symbol.SGD { + if amount*0.001 < 10 { + fee = 10 + } else { + fee = amount * 0.001 + } + } + + return fee +} + +func getInternationalBankDepositFee(currency string, amount float64) float64 { + var fee float64 + + if currency == symbol.USD { + if amount*0.001 < 10 { + fee = 10 + } else { + fee = amount * 0.001 + } + } else if currency == symbol.CAD { + if amount*0.005 < 10 { + fee = 2 + } else { + fee = amount * 0.005 + } + } + + return fee +} diff --git a/exchanges/coinut/coinut_test.go b/exchanges/coinut/coinut_test.go index 0a70b888..2eac1dd4 100644 --- a/exchanges/coinut/coinut_test.go +++ b/exchanges/coinut/coinut_test.go @@ -5,6 +5,8 @@ import ( "time" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var c COINUT @@ -42,3 +44,133 @@ func TestGetInstruments(t *testing.T) { t.Error("Test failed - GetInstruments() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + } +} + +func TestGetFee(t *testing.T) { + c.SetDefaults() + TestSetup(t) + t.Parallel() + + var feeBuilder = setFeeBuilder() + + // CryptocurrencyTradeFee Basic + if resp, err := c.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0010), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := c.GetFee(feeBuilder); resp != float64(1000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(1000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := c.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := c.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := c.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := c.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.EUR + if resp, err := c.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := c.GetFee(feeBuilder); resp != float64(10) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(10), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.SGD + if resp, err := c.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := c.GetFee(feeBuilder); resp != float64(10) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(10), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.CAD + if resp, err := c.GetFee(feeBuilder); resp != float64(2) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.SGD + if resp, err := c.GetFee(feeBuilder); resp != float64(10) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(10), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.CAD + if resp, err := c.GetFee(feeBuilder); resp != float64(2) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2), resp) + t.Error(err) + } +} diff --git a/exchanges/coinut/coinut_wrapper.go b/exchanges/coinut/coinut_wrapper.go index 238f46f4..436ca2b3 100644 --- a/exchanges/coinut/coinut_wrapper.go +++ b/exchanges/coinut/coinut_wrapper.go @@ -194,3 +194,8 @@ func (c *COINUT) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curr func (c *COINUT) GetWebsocket() (*exchange.Websocket, error) { return c.Websocket, nil } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (c *COINUT) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return c.GetFee(feeBuilder) +} diff --git a/exchanges/exchange.go b/exchanges/exchange.go index f19d46f9..018d31f7 100644 --- a/exchanges/exchange.go +++ b/exchanges/exchange.go @@ -29,6 +29,59 @@ const ( DefaultHTTPTimeout = time.Second * 15 ) +// FeeType custom type for calculating fees based on method +type FeeType string + +// InternationalBankTransactionType custom type for calculating fees based on fiat transaction types +type InternationalBankTransactionType string + +// Const declarations for fee types +const ( + BankFee FeeType = "bankFee" + InternationalBankDepositFee FeeType = "internationalBankDepositFee" + InternationalBankWithdrawalFee FeeType = "internationalBankWithdrawalFee" + CryptocurrencyTradeFee FeeType = "cryptocurrencyTradeFee" + CyptocurrencyDepositFee FeeType = "cyptocurrencyDepositFee" + CryptocurrencyWithdrawalFee FeeType = "cryptocurrencyWithdrawalFee" +) + +// Const declarations for international transaction types +const ( + WireTransfer InternationalBankTransactionType = "wireTransfer" + PerfectMoney InternationalBankTransactionType = "perfectMoney" + Neteller InternationalBankTransactionType = "neteller" + AdvCash InternationalBankTransactionType = "advCash" + Payeer InternationalBankTransactionType = "payeer" + Skrill InternationalBankTransactionType = "skrill" + Simplex InternationalBankTransactionType = "simplex" + SEPA InternationalBankTransactionType = "sepa" + RapidTransfer InternationalBankTransactionType = "rapidTransfer" + MisterTangoSEPA InternationalBankTransactionType = "misterTangoSepa" + Qiwi InternationalBankTransactionType = "qiwi" + VisaMastercard InternationalBankTransactionType = "visaMastercard" + WebMoney InternationalBankTransactionType = "webMoney" + Capitalist InternationalBankTransactionType = "capitalist" + WesternUnion InternationalBankTransactionType = "westernUnion" + MoneyGram InternationalBankTransactionType = "moneyGram" + Contact InternationalBankTransactionType = "contact" +) + +// FeeBuilder is the type which holds all parameters required to calculate a fee for an exchange +type FeeBuilder struct { + FeeType FeeType + //Used for calculating crypto trading fees, deposits & withdrawals + FirstCurrency string + SecondCurrency string + Delimiter string + IsMaker bool + // Fiat currency used for bank deposits & withdrawals + CurrencyItem string + BankTransactionType InternationalBankTransactionType + // Used to multiply for fee calculations + PurchasePrice float64 + Amount float64 +} + // AccountInfo is a Generic type to hold each exchange's holdings in // all enabled currencies type AccountInfo struct { diff --git a/exchanges/exmo/exmo.go b/exchanges/exmo/exmo.go index dd6be332..988cf24a 100644 --- a/exchanges/exmo/exmo.go +++ b/exchanges/exmo/exmo.go @@ -11,6 +11,7 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" exchange "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -363,3 +364,161 @@ func (e *EXMO) SendAuthenticatedHTTPRequest(method, endpoint string, vals url.Va return e.SendPayload(method, path, headers, strings.NewReader(payload), result, true, e.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (e *EXMO) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = e.calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount) + case exchange.CryptocurrencyWithdrawalFee: + fee = getCryptocurrencyWithdrawalFee(feeBuilder.FirstCurrency) + case exchange.InternationalBankWithdrawalFee: + fee = getInternationalBankWithdrawalFee(feeBuilder.CurrencyItem, feeBuilder.Amount, feeBuilder.BankTransactionType) + case exchange.InternationalBankDepositFee: + fee = getInternationalBankDepositFee(feeBuilder.CurrencyItem, feeBuilder.Amount, feeBuilder.BankTransactionType) + } + + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func getCryptocurrencyWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} + +func (e *EXMO) calculateTradingFee(purchasePrice, amount float64) float64 { + fee := 0.002 + return fee * amount * purchasePrice +} + +func calculateTradingFee(purchasePrice, amount float64) float64 { + fee := 0.002 + return fee * amount * purchasePrice +} + +func getInternationalBankWithdrawalFee(currency string, amount float64, bankTransactionType exchange.InternationalBankTransactionType) float64 { + var fee float64 + + switch bankTransactionType { + case exchange.WireTransfer: + if currency == symbol.RUB { + fee = 3200 + } else if currency == symbol.PLN { + fee = 125 + } else if currency == symbol.TRY { + fee = 0 + } + case exchange.PerfectMoney: + switch currency { + case symbol.USD: + fee = 0.01 * amount + case symbol.EUR: + fee = 0.0195 * amount + } + case exchange.Neteller: + switch currency { + case symbol.USD: + fee = 0.0195 * amount + case symbol.EUR: + fee = 0.0195 * amount + } + case exchange.AdvCash: + switch currency { + case symbol.USD: + fee = 0.0295 * amount + case symbol.EUR: + fee = 0.03 * amount + case symbol.RUB: + fee = 0.0195 * amount + case symbol.UAH: + fee = 0.0495 * amount + } + case exchange.Payeer: + switch currency { + case symbol.USD: + fee = 0.0395 * amount + case symbol.EUR: + fee = 0.01 * amount + case symbol.RUB: + fee = 0.0595 * amount + } + case exchange.Skrill: + switch currency { + case symbol.USD: + fee = 0.0145 * amount + case symbol.EUR: + fee = 0.03 * amount + case symbol.TRY: + fee = 0 + } + case exchange.VisaMastercard: + switch currency { + case symbol.USD: + fee = 0.06 * amount + case symbol.EUR: + fee = 0.06 * amount + case symbol.PLN: + fee = 0.06 * amount + } + } + + return fee +} + +func getInternationalBankDepositFee(currency string, amount float64, bankTransactionType exchange.InternationalBankTransactionType) float64 { + var fee float64 + switch bankTransactionType { + case exchange.WireTransfer: + if currency == symbol.RUB { + fee = 1600 + } else if currency == symbol.PLN { + fee = 30 + } else if currency == symbol.TRY { + fee = 0 + } + case exchange.Neteller: + switch currency { + case symbol.USD: + fee = (0.035 * amount) + 0.29 + case symbol.EUR: + fee = (0.035 * amount) + 0.25 + } + case exchange.AdvCash: + switch currency { + case symbol.USD: + fee = 0.0295 * amount + case symbol.EUR: + fee = 0.01 * amount + case symbol.RUB: + fee = 0.0495 * amount + case symbol.UAH: + fee = 0.01 * amount + } + case exchange.Payeer: + switch currency { + case symbol.USD: + fee = 0.0195 * amount + case symbol.EUR: + fee = 0.0295 * amount + case symbol.RUB: + fee = 0.0345 * amount + } + case exchange.Skrill: + switch currency { + case symbol.USD: + fee = (0.0495 * amount) + 0.36 + case symbol.EUR: + fee = (0.0295 * amount) + 0.29 + case symbol.PLN: + fee = (0.035 * amount) + 1.21 + case symbol.TRY: + fee = 0 + } + } + + return fee +} diff --git a/exchanges/exmo/exmo_test.go b/exchanges/exmo/exmo_test.go index caf51dab..4f09e25b 100644 --- a/exchanges/exmo/exmo_test.go +++ b/exchanges/exmo/exmo_test.go @@ -1,6 +1,11 @@ package exmo -import "testing" +import ( + "testing" + + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" +) const ( APIKey = "" @@ -96,3 +101,135 @@ func TestGetDepositAddress(t *testing.T) { t.Errorf("Test failed. Err: %s", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + e.SetDefaults() + TestSetup(t) + t.Parallel() + + var feeBuilder = setFeeBuilder() + + // CryptocurrencyTradeFee Basic + if resp, err := e.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := e.GetFee(feeBuilder); resp != float64(2000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := e.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := e.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := e.GetFee(feeBuilder); resp != float64(0.0005) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0005), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := e.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := e.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.RUB + if resp, err := e.GetFee(feeBuilder); resp != float64(1600) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(1600), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + feeBuilder.CurrencyItem = symbol.PLN + if resp, err := e.GetFee(feeBuilder); resp != float64(30) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(30), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.PLN + if resp, err := e.GetFee(feeBuilder); resp != float64(125) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(125), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.TRY + if resp, err := e.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.EUR + if resp, err := e.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.RUB + if resp, err := e.GetFee(feeBuilder); resp != float64(3200) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(3200), resp) + t.Error(err) + } +} diff --git a/exchanges/exmo/exmo_types.go b/exchanges/exmo/exmo_types.go index c478d2ad..4b59c7d2 100644 --- a/exchanges/exmo/exmo_types.go +++ b/exchanges/exmo/exmo_types.go @@ -1,5 +1,7 @@ package exmo +import "github.com/thrasher-/gocryptotrader/currency/symbol" + // Trades holds trade data type Trades struct { TradeID int64 `json:"trade_id"` @@ -142,3 +144,33 @@ type WalletHistory struct { Account string `json:"account,string"` } } + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change +var WithdrawalFees = map[string]float64{ + symbol.BTC: 0.0005, + symbol.LTC: 0.01, + symbol.DOGE: 1, + symbol.DASH: 0.01, + symbol.ETH: 0.01, + symbol.WAVES: 0.001, + symbol.ZEC: 0.001, + symbol.USDT: 5, + symbol.XMR: 0.05, + symbol.XRP: 0.02, + symbol.KICK: 50, + symbol.ETC: 0.01, + symbol.BCH: 0.001, + symbol.BTG: 0.001, + symbol.HBZ: 65, + symbol.BTCZ: 5, + symbol.DXT: 20, + symbol.STQ: 100, + symbol.XLM: 0.001, + symbol.OMG: 0.5, + symbol.TRX: 1, + symbol.ADA: 1, + symbol.INK: 50, + symbol.ZRX: 1, + symbol.GNT: 1, +} diff --git a/exchanges/exmo/exmo_wrapper.go b/exchanges/exmo/exmo_wrapper.go index 72d7f546..75732255 100644 --- a/exchanges/exmo/exmo_wrapper.go +++ b/exchanges/exmo/exmo_wrapper.go @@ -229,3 +229,8 @@ func (e *EXMO) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curren func (e *EXMO) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (e *EXMO) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return e.GetFee(feeBuilder) +} diff --git a/exchanges/gateio/gateio.go b/exchanges/gateio/gateio.go index e80f69c5..6d220b46 100644 --- a/exchanges/gateio/gateio.go +++ b/exchanges/gateio/gateio.go @@ -403,3 +403,41 @@ func (g *Gateio) SendAuthenticatedHTTPRequest(method, endpoint, param string, re return g.SendPayload(method, url, headers, strings.NewReader(param), result, true, g.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (g *Gateio) GetFee(feeBuilder exchange.FeeBuilder) (fee float64, err error) { + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + feePairs, err := g.GetMarketInfo() + if err != nil { + return 0, err + } + currencyPair := feeBuilder.FirstCurrency + feeBuilder.Delimiter + feeBuilder.SecondCurrency + var feeForPair float64 + for _, i := range feePairs.Pairs { + if strings.EqualFold(currencyPair, i.Symbol) { + feeForPair = i.Fee + } + } + if feeForPair == 0 { + return 0, fmt.Errorf("Currency: '%s' failed to find fee data", currencyPair) + } + fee = calculateTradingFee(feeForPair, feeBuilder.PurchasePrice, feeBuilder.Amount) + case exchange.CryptocurrencyWithdrawalFee: + fee = getCryptocurrencyWithdrawalFee(feeBuilder.FirstCurrency) + } + + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(feeForPair, purchasePrice, amount float64) float64 { + return (feeForPair / 100) * purchasePrice * amount +} + +func getCryptocurrencyWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} diff --git a/exchanges/gateio/gateio_test.go b/exchanges/gateio/gateio_test.go index b8521b9b..6915d062 100644 --- a/exchanges/gateio/gateio_test.go +++ b/exchanges/gateio/gateio_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) // Please supply your own APIKEYS here for due diligence testing @@ -139,3 +141,97 @@ func TestGetSpotKline(t *testing.T) { t.Errorf("Test failed - Gateio GetSpotKline: %s", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "_", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.USDT, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + g.SetDefaults() + TestSetup(t) + + var feeBuilder = setFeeBuilder() + if apiKey != "" && apiSecret != "" { + // CryptocurrencyTradeFee Basic + if resp, err := g.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := g.GetFee(feeBuilder); resp != float64(2000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := g.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := g.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := g.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/gateio/gateio_types.go b/exchanges/gateio/gateio_types.go index b2526246..0c4b9900 100644 --- a/exchanges/gateio/gateio_types.go +++ b/exchanges/gateio/gateio_types.go @@ -1,6 +1,10 @@ package gateio -import "time" +import ( + "time" + + "github.com/thrasher-/gocryptotrader/currency/symbol" +) // SpotNewOrderRequestParamsType order type (buy or sell) type SpotNewOrderRequestParamsType string @@ -125,3 +129,212 @@ type SpotNewOrderResponse struct { FilledAmount float64 `json:"filledAmount,string"` // The filled amount Filledrate float64 `json:"filledRate,string"` // FilledPrice } + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change +var WithdrawalFees = map[string]float64{ + symbol.USDT: 10, + symbol.USDT_ETH: 10, + symbol.BTC: 0.001, + symbol.BCH: 0.0006, + symbol.BTG: 0.002, + symbol.LTC: 0.002, + symbol.ZEC: 0.001, + symbol.ETH: 0.003, + symbol.ETC: 0.01, + symbol.DASH: 0.02, + symbol.QTUM: 0.1, + symbol.QTUM_ETH: 0.1, + symbol.DOGE: 50, + symbol.REP: 0.1, + symbol.BAT: 10, + symbol.SNT: 30, + symbol.BTM: 10, + symbol.BTM_ETH: 10, + symbol.CVC: 5, + symbol.REQ: 20, + symbol.RDN: 1, + symbol.STX: 3, + symbol.KNC: 1, + symbol.LINK: 8, + symbol.FIL: 0.1, + symbol.CDT: 20, + symbol.AE: 1, + symbol.INK: 10, + symbol.BOT: 5, + symbol.POWR: 5, + symbol.WTC: 0.2, + symbol.VET: 10, + symbol.RCN: 5, + symbol.PPT: 0.1, + symbol.ARN: 2, + symbol.BNT: 0.5, + symbol.VERI: 0.005, + symbol.MCO: 0.1, + symbol.MDA: 0.5, + symbol.FUN: 50, + symbol.DATA: 10, + symbol.RLC: 1, + symbol.ZSC: 20, + symbol.WINGS: 2, + symbol.GVT: 0.2, + symbol.KICK: 5, + symbol.CTR: 1, + symbol.HC: 0.2, + symbol.QBT: 5, + symbol.QSP: 5, + symbol.BCD: 0.02, + symbol.MED: 100, + symbol.QASH: 1, + symbol.DGD: 0.05, + symbol.GNT: 10, + symbol.MDS: 20, + symbol.SBTC: 0.05, + symbol.MANA: 50, + symbol.GOD: 0.1, + symbol.BCX: 30, + symbol.SMT: 50, + symbol.BTF: 0.1, + symbol.IOTA: 0.1, + symbol.NAS: 0.5, + symbol.NAS_ETH: 0.5, + symbol.TSL: 10, + symbol.ADA: 1, + symbol.LSK: 0.1, + symbol.WAVES: 0.1, + symbol.BIFI: 0.2, + symbol.XTZ: 0.1, + symbol.BNTY: 10, + symbol.ICX: 0.5, + symbol.LEND: 20, + symbol.LUN: 0.2, + symbol.ELF: 2, + symbol.SALT: 0.2, + symbol.FUEL: 2, + symbol.DRGN: 2, + symbol.GTC: 2, + symbol.MDT: 2, + symbol.QUN: 2, + symbol.GNX: 2, + symbol.DDD: 10, + symbol.OST: 4, + symbol.BTO: 10, + symbol.TIO: 10, + symbol.THETA: 10, + symbol.SNET: 10, + symbol.OCN: 10, + symbol.ZIL: 10, + symbol.RUFF: 10, + symbol.TNC: 10, + symbol.COFI: 10, + symbol.ZPT: 0.1, + symbol.JNT: 10, + symbol.GXS: 1, + symbol.MTN: 10, + symbol.BLZ: 2, + symbol.GEM: 2, + symbol.DADI: 2, + symbol.ABT: 2, + symbol.LEDU: 10, + symbol.RFR: 10, + symbol.XLM: 1, + symbol.MOBI: 1, + symbol.ONT: 1, + symbol.NEO: 0, + symbol.GAS: 0.02, + symbol.DBC: 10, + symbol.QLC: 10, + symbol.MKR: 0.003, + symbol.MKR_OLD: 0.003, + symbol.DAI: 2, + symbol.LRC: 10, + symbol.OAX: 10, + symbol.ZRX: 10, + symbol.PST: 5, + symbol.TNT: 20, + symbol.LLT: 10, + symbol.DNT: 1, + symbol.DPY: 2, + symbol.BCDN: 20, + symbol.STORJ: 3, + symbol.OMG: 0.2, + symbol.PAY: 1, + symbol.EOS: 0.1, + symbol.EON: 20, + symbol.IQ: 20, + symbol.EOSDAC: 20, + symbol.TIPS: 100, + symbol.XRP: 1, + symbol.CNC: 0.1, + symbol.TIX: 0.1, + symbol.XMR: 0.05, + symbol.BTS: 1, + symbol.XTC: 10, + symbol.BU: 0.1, + symbol.DCR: 0.02, + symbol.BCN: 10, + symbol.XMC: 0.05, + symbol.PPS: 0.01, + symbol.BOE: 5, + symbol.PLY: 10, + symbol.MEDX: 100, + symbol.TRX: 0.1, + symbol.SMT_ETH: 50, + symbol.CS: 10, + symbol.MAN: 10, + symbol.REM: 10, + symbol.LYM: 10, + symbol.INSTAR: 10, + symbol.BFT: 10, + symbol.IHT: 10, + symbol.SENC: 10, + symbol.TOMO: 10, + symbol.ELEC: 10, + symbol.SHIP: 10, + symbol.TFD: 10, + symbol.HAV: 10, + symbol.HUR: 10, + symbol.LST: 10, + symbol.LINO: 10, + symbol.SWTH: 5, + symbol.NKN: 5, + symbol.SOUL: 5, + symbol.GALA_NEO: 5, + symbol.LRN: 5, + symbol.ADD: 20, + symbol.MEETONE: 5, + symbol.DOCK: 20, + symbol.GSE: 20, + symbol.RATING: 20, + symbol.HSC: 100, + symbol.HIT: 100, + symbol.DX: 100, + symbol.BXC: 100, + symbol.PAX: 5, + symbol.GARD: 100, + symbol.FTI: 100, + symbol.SOP: 100, + symbol.LEMO: 20, + symbol.NPXS: 40, + symbol.QKC: 20, + symbol.IOTX: 20, + symbol.RED: 20, + symbol.LBA: 20, + symbol.KAN: 20, + symbol.OPEN: 20, + symbol.MITH: 20, + symbol.SKM: 20, + symbol.XVG: 20, + symbol.NANO: 20, + symbol.NBAI: 20, + symbol.UPP: 20, + symbol.ATMI: 20, + symbol.TMT: 20, + symbol.HT: 1, + symbol.BNB: 0.3, + symbol.BBK: 20, + symbol.EDR: 20, + symbol.MET: 0.3, + symbol.TCT: 20, + symbol.EXC: 10, +} diff --git a/exchanges/gateio/gateio_wrapper.go b/exchanges/gateio/gateio_wrapper.go index db1852e4..af4c96c6 100644 --- a/exchanges/gateio/gateio_wrapper.go +++ b/exchanges/gateio/gateio_wrapper.go @@ -180,3 +180,8 @@ func (g *Gateio) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curr func (g *Gateio) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (g *Gateio) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return g.GetFee(feeBuilder) +} diff --git a/exchanges/gemini/gemini.go b/exchanges/gemini/gemini.go index 4a3e5627..6fe0eca8 100644 --- a/exchanges/gemini/gemini.go +++ b/exchanges/gemini/gemini.go @@ -40,6 +40,7 @@ const ( geminiNewAddress = "newAddress" geminiWithdraw = "withdraw/" geminiHeartbeat = "heartbeat" + geminiVolume = "notionalvolume" // gemini limit rates geminiAuthRate = 600 @@ -389,6 +390,14 @@ func (g *Gemini) GetTradeHistory(currencyPair string, timestamp int64) ([]TradeH g.SendAuthenticatedHTTPRequest("POST", geminiMyTrades, request, &response) } +// GetNotionalVolume returns the volume in price currency that has been traded across all pairs over a period of 30 days +func (g *Gemini) GetNotionalVolume() (NotionalVolume, error) { + response := NotionalVolume{} + + return response, + g.SendAuthenticatedHTTPRequest("POST", geminiVolume, nil, &response) +} + // GetTradeVolume returns a multi-arrayed volume response func (g *Gemini) GetTradeVolume() ([][]TradeVolume, error) { response := [][]TradeVolume{} @@ -496,3 +505,35 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(method, path string, params map[st return g.SendPayload(method, g.APIUrl+"/v1/"+path, headers, strings.NewReader(""), result, true, g.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (g *Gemini) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + notionVolume, err := g.GetNotionalVolume() + if err != nil { + return 0, err + } + fee = calculateTradingFee(notionVolume, feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + case exchange.CryptocurrencyWithdrawalFee: + // TODO: no free transactions after 10; Need database to know how many trades have been done + // Could do via trade history, but would require analysis of response and dates to determine level of fee + } + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(notionVolume NotionalVolume, purchasePrice, amount float64, isMaker bool) float64 { + var volumeFee float64 + if isMaker { + volumeFee = (float64(notionVolume.MakerFee) / 100) + } else { + volumeFee = (float64(notionVolume.TakerFee) / 100) + } + + return volumeFee * amount * purchasePrice +} diff --git a/exchanges/gemini/gemini_test.go b/exchanges/gemini/gemini_test.go index 972a0f5f..b583dca4 100644 --- a/exchanges/gemini/gemini_test.go +++ b/exchanges/gemini/gemini_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) // Please enter sandbox API keys & assigned roles for better testing procedures @@ -58,14 +60,6 @@ func TestSetup(t *testing.T) { Session[2].Setup(geminiConfig) } -// func TestSandbox(t *testing.T) { -// t.Parallel() -// g.Sandbox(1) -// if Management[1].URL != geminiSandboxAPIURL { -// t.Error("Test Failed - Sandbox() error") -// } -// } - func TestGetSymbols(t *testing.T) { t.Parallel() _, err := Session[1].GetSymbols() @@ -102,6 +96,16 @@ func TestGetTrades(t *testing.T) { } } +func TestGetNotionalVolume(t *testing.T) { + if apiKey2 != "" && apiSecret2 != "" { + t.Parallel() + _, err := Session[2].GetNotionalVolume() + if err != nil { + t.Error("Test Failed - GetNotionalVolume() error", err) + } + } +} + func TestGetAuction(t *testing.T) { t.Parallel() _, err := Session[1].GetAuction("btcusd") @@ -213,3 +217,95 @@ func TestPostHeartbeat(t *testing.T) { t.Error("Test Failed - PostHeartbeat() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "_", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + + var feeBuilder = setFeeBuilder() + if apiKey1 != "" && apiSecret1 != "" { + // CryptocurrencyTradeFee Basic + if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0.01) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.01), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := Session[1].GetFee(feeBuilder); resp != float64(100) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(100), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0.01) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.01), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := Session[1].GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/gemini/gemini_types.go b/exchanges/gemini/gemini_types.go index f4edca5e..79ddcfb4 100644 --- a/exchanges/gemini/gemini_types.go +++ b/exchanges/gemini/gemini_types.go @@ -140,6 +140,24 @@ type TradeVolume struct { SellTakerCount float64 `json:"sell_taker_count"` } +// NotionalVolume api call for fees +type NotionalVolume struct { + MakerFee int64 `json:"maker_fee_bps"` + TakerFee int64 `json:"taker_fee_bps"` + AuctionFee int64 `json:"auction_fee_bps"` + ThirtyDayVolume float64 `json:"notional_30d_volume"` + LastedUpdated int64 `json:"last_updated_ms"` + AccountID int64 `json:"account_id"` + Date string `json:"date"` + OneDayNotionalVolumes []OneDayNotionalVolume `json:"notional_1d_volume"` +} + +// OneDayNotionalVolume Contains the notioanl volume for a single day +type OneDayNotionalVolume struct { + Date string `json:"date"` + NotationalVolume float64 `json:"notional_volume"` +} + // Balance is a simple balance type type Balance struct { Currency string `json:"currency"` diff --git a/exchanges/gemini/gemini_wrapper.go b/exchanges/gemini/gemini_wrapper.go index a20a55bb..4edfcf27 100644 --- a/exchanges/gemini/gemini_wrapper.go +++ b/exchanges/gemini/gemini_wrapper.go @@ -180,3 +180,8 @@ func (g *Gemini) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curr func (g *Gemini) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (g *Gemini) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return g.GetFee(feeBuilder) +} diff --git a/exchanges/hitbtc/hitbtc.go b/exchanges/hitbtc/hitbtc.go index 8dc1af9f..efd5fc76 100644 --- a/exchanges/hitbtc/hitbtc.go +++ b/exchanges/hitbtc/hitbtc.go @@ -12,6 +12,7 @@ import ( "github.com/gorilla/websocket" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -54,65 +55,65 @@ type HitBTC struct { } // SetDefaults sets default settings for hitbtc -func (p *HitBTC) SetDefaults() { - p.Name = "HitBTC" - p.Enabled = false - p.Fee = 0 - p.Verbose = false - p.RESTPollingDelay = 10 - p.RequestCurrencyPairFormat.Delimiter = "" - p.RequestCurrencyPairFormat.Uppercase = true - p.ConfigCurrencyPairFormat.Delimiter = "-" - p.ConfigCurrencyPairFormat.Uppercase = true - p.AssetTypes = []string{ticker.Spot} - p.SupportsAutoPairUpdating = true - p.SupportsRESTTickerBatching = true - p.Requester = request.New(p.Name, +func (h *HitBTC) SetDefaults() { + h.Name = "HitBTC" + h.Enabled = false + h.Fee = 0 + h.Verbose = false + h.RESTPollingDelay = 10 + h.RequestCurrencyPairFormat.Delimiter = "" + h.RequestCurrencyPairFormat.Uppercase = true + h.ConfigCurrencyPairFormat.Delimiter = "-" + h.ConfigCurrencyPairFormat.Uppercase = true + h.AssetTypes = []string{ticker.Spot} + h.SupportsAutoPairUpdating = true + h.SupportsRESTTickerBatching = true + h.Requester = request.New(h.Name, request.NewRateLimit(time.Second, hitbtcAuthRate), request.NewRateLimit(time.Second, hitbtcUnauthRate), common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout)) - p.APIUrlDefault = apiURL - p.APIUrl = p.APIUrlDefault - p.WebsocketInit() + h.APIUrlDefault = apiURL + h.APIUrl = h.APIUrlDefault + h.WebsocketInit() } // Setup sets user exchange configuration settings -func (p *HitBTC) Setup(exch config.ExchangeConfig) { +func (h *HitBTC) Setup(exch config.ExchangeConfig) { if !exch.Enabled { - p.SetEnabled(false) + h.SetEnabled(false) } else { - p.Enabled = true - p.AuthenticatedAPISupport = exch.AuthenticatedAPISupport - p.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) - p.SetHTTPClientTimeout(exch.HTTPTimeout) - p.SetHTTPClientUserAgent(exch.HTTPUserAgent) - p.RESTPollingDelay = exch.RESTPollingDelay // Max 60000ms - p.Verbose = exch.Verbose - p.Websocket.SetEnabled(exch.Websocket) - p.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",") - p.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",") - p.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",") - err := p.SetCurrencyPairFormat() + h.Enabled = true + h.AuthenticatedAPISupport = exch.AuthenticatedAPISupport + h.SetAPIKeys(exch.APIKey, exch.APISecret, "", false) + h.SetHTTPClientTimeout(exch.HTTPTimeout) + h.SetHTTPClientUserAgent(exch.HTTPUserAgent) + h.RESTPollingDelay = exch.RESTPollingDelay // Max 60000ms + h.Verbose = exch.Verbose + h.Websocket.SetEnabled(exch.Websocket) + h.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",") + h.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",") + h.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",") + err := h.SetCurrencyPairFormat() if err != nil { log.Fatal(err) } - err = p.SetAssetTypes() + err = h.SetAssetTypes() if err != nil { log.Fatal(err) } - err = p.SetAutoPairDefaults() + err = h.SetAutoPairDefaults() if err != nil { log.Fatal(err) } - err = p.SetAPIURL(exch) + err = h.SetAPIURL(exch) if err != nil { log.Fatal(err) } - err = p.SetClientProxyAddress(exch.ProxyAddress) + err = h.SetClientProxyAddress(exch.ProxyAddress) if err != nil { log.Fatal(err) } - err = p.WebsocketSetup(p.WsConnect, + err = h.WebsocketSetup(h.WsConnect, exch.Name, exch.Websocket, hitbtcWebsocketAddress, @@ -123,25 +124,20 @@ func (p *HitBTC) Setup(exch config.ExchangeConfig) { } } -// GetFee returns the fee for hitbtc -func (p *HitBTC) GetFee() float64 { - return p.Fee -} - // Public Market Data // https://api.hitbtc.com/?python#market-data // GetCurrencies returns the actual list of available currencies, tokens, ICO // etc. -func (p *HitBTC) GetCurrencies(currency string) (map[string]Currencies, error) { +func (h *HitBTC) GetCurrencies() (map[string]Currencies, error) { type Response struct { Data []Currencies } resp := Response{} - path := fmt.Sprintf("%s/%s/%s", p.APIUrl, apiV2Currency, currency) + path := fmt.Sprintf("%s/%s", h.APIUrl, apiV2Currency) ret := make(map[string]Currencies) - err := p.SendHTTPRequest(path, &resp.Data) + err := h.SendHTTPRequest(path, &resp.Data) if err != nil { return ret, err } @@ -152,17 +148,29 @@ func (p *HitBTC) GetCurrencies(currency string) (map[string]Currencies, error) { return ret, err } +// GetCurrency returns the actual list of available currencies, tokens, ICO +// etc. +func (h *HitBTC) GetCurrency(currency string) (Currencies, error) { + type Response struct { + Data Currencies + } + resp := Response{} + path := fmt.Sprintf("%s/%s/%s", h.APIUrl, apiV2Currency, currency) + + return resp.Data, h.SendHTTPRequest(path, &resp.Data) +} + // GetSymbols Return the actual list of currency symbols (currency pairs) traded // on HitBTC exchange. The first listed currency of a symbol is called the base // currency, and the second currency is called the quote currency. The currency // pair indicates how much of the quote currency is needed to purchase one unit // of the base currency. -func (p *HitBTC) GetSymbols(symbol string) ([]string, error) { +func (h *HitBTC) GetSymbols(symbol string) ([]string, error) { resp := []Symbol{} - path := fmt.Sprintf("%s/%s/%s", p.APIUrl, apiV2Symbol, symbol) + path := fmt.Sprintf("%s/%s/%s", h.APIUrl, apiV2Symbol, symbol) ret := make([]string, 0, len(resp)) - err := p.SendHTTPRequest(path, &resp) + err := h.SendHTTPRequest(path, &resp) if err != nil { return ret, err } @@ -175,24 +183,24 @@ func (p *HitBTC) GetSymbols(symbol string) ([]string, error) { // GetSymbolsDetailed is the same as above but returns an array of symbols with // all their details. -func (p *HitBTC) GetSymbolsDetailed() ([]Symbol, error) { +func (h *HitBTC) GetSymbolsDetailed() ([]Symbol, error) { resp := []Symbol{} - path := fmt.Sprintf("%s/%s", p.APIUrl, apiV2Symbol) + path := fmt.Sprintf("%s/%s", h.APIUrl, apiV2Symbol) - return resp, p.SendHTTPRequest(path, &resp) + return resp, h.SendHTTPRequest(path, &resp) } // GetTicker returns ticker information -func (p *HitBTC) GetTicker(symbol string) (map[string]Ticker, error) { +func (h *HitBTC) GetTicker(symbol string) (map[string]Ticker, error) { resp1 := []TickerResponse{} resp2 := TickerResponse{} ret := make(map[string]TickerResponse) result := make(map[string]Ticker) - path := fmt.Sprintf("%s/%s/%s", p.APIUrl, apiV2Ticker, symbol) + path := fmt.Sprintf("%s/%s/%s", h.APIUrl, apiV2Ticker, symbol) var err error if symbol == "" { - err = p.SendHTTPRequest(path, &resp1) + err = h.SendHTTPRequest(path, &resp1) if err != nil { return nil, err } @@ -203,7 +211,7 @@ func (p *HitBTC) GetTicker(symbol string) (map[string]Ticker, error) { } } } else { - err = p.SendHTTPRequest(path, &resp2) + err = h.SendHTTPRequest(path, &resp2) ret[resp2.Symbol] = resp2 } @@ -245,12 +253,12 @@ func (p *HitBTC) GetTicker(symbol string) (map[string]Ticker, error) { } // GetTrades returns trades from hitbtc -func (p *HitBTC) GetTrades(currencyPair, from, till, limit, offset, by, sort string) ([]TradeHistory, error) { +func (h *HitBTC) GetTrades(currencyPair, from, till, limit, offset, by, sort string) ([]TradeHistory, error) { // start Number or Datetime // end Number or Datetime // limit Number // offset Number - // by Filtration definition. Accepted values: id, timestamp. Default timestamp + // by Filtration definition. Accepted values: id, timestamh. Default timestamp // sort Default DESC vals := url.Values{} @@ -279,14 +287,14 @@ func (p *HitBTC) GetTrades(currencyPair, from, till, limit, offset, by, sort str } resp := []TradeHistory{} - path := fmt.Sprintf("%s/%s/%s?%s", p.APIUrl, apiV2Trades, currencyPair, vals.Encode()) + path := fmt.Sprintf("%s/%s/%s?%s", h.APIUrl, apiV2Trades, currencyPair, vals.Encode()) - return resp, p.SendHTTPRequest(path, &resp) + return resp, h.SendHTTPRequest(path, &resp) } // GetOrderbook an order book is an electronic list of buy and sell orders for a // specific symbol, organized by price level. -func (p *HitBTC) GetOrderbook(currencyPair string, limit int) (Orderbook, error) { +func (h *HitBTC) GetOrderbook(currencyPair string, limit int) (Orderbook, error) { // limit Limit of orderbook levels, default 100. Set 0 to view full orderbook levels vals := url.Values{} @@ -295,9 +303,9 @@ func (p *HitBTC) GetOrderbook(currencyPair string, limit int) (Orderbook, error) } resp := OrderbookResponse{} - path := fmt.Sprintf("%s/%s/%s?%s", p.APIUrl, apiV2Orderbook, currencyPair, vals.Encode()) + path := fmt.Sprintf("%s/%s/%s?%s", h.APIUrl, apiV2Orderbook, currencyPair, vals.Encode()) - err := p.SendHTTPRequest(path, &resp) + err := h.SendHTTPRequest(path, &resp) if err != nil { return Orderbook{}, err } @@ -315,7 +323,7 @@ func (p *HitBTC) GetOrderbook(currencyPair string, limit int) (Orderbook, error) // GetCandles returns candles which is used for OHLC a specific symbol. // Note: Result contain candles only with non zero volume. -func (p *HitBTC) GetCandles(currencyPair, limit, period string) ([]ChartData, error) { +func (h *HitBTC) GetCandles(currencyPair, limit, period string) ([]ChartData, error) { // limit Limit of candles, default 100. // period One of: M1 (one minute), M3, M5, M15, M30, H1, H4, D1, D7, 1M (one month). Default is M30 (30 minutes). vals := url.Values{} @@ -329,18 +337,18 @@ func (p *HitBTC) GetCandles(currencyPair, limit, period string) ([]ChartData, er } resp := []ChartData{} - path := fmt.Sprintf("%s/%s/%s?%s", p.APIUrl, apiV2Candles, currencyPair, vals.Encode()) + path := fmt.Sprintf("%s/%s/%s?%s", h.APIUrl, apiV2Candles, currencyPair, vals.Encode()) - return resp, p.SendHTTPRequest(path, &resp) + return resp, h.SendHTTPRequest(path, &resp) } // Authenticated Market Data // https://api.hitbtc.com/?python#market-data // GetBalances returns full balance for your account -func (p *HitBTC) GetBalances() (map[string]Balance, error) { +func (h *HitBTC) GetBalances() (map[string]Balance, error) { result := []Balance{} - err := p.SendAuthenticatedHTTPRequest("GET", apiV2Balance, url.Values{}, &result) + err := h.SendAuthenticatedHTTPRequest("GET", apiV2Balance, url.Values{}, &result) ret := make(map[string]Balance) if err != nil { @@ -355,31 +363,31 @@ func (p *HitBTC) GetBalances() (map[string]Balance, error) { } // GetDepositAddresses returns a deposit address for a specific currency -func (p *HitBTC) GetDepositAddresses(currency string) (DepositCryptoAddresses, error) { +func (h *HitBTC) GetDepositAddresses(currency string) (DepositCryptoAddresses, error) { resp := DepositCryptoAddresses{} - err := p.SendAuthenticatedHTTPRequest("GET", apiV2CryptoAddress+"/"+currency, url.Values{}, &resp) + err := h.SendAuthenticatedHTTPRequest("GET", apiV2CryptoAddress+"/"+currency, url.Values{}, &resp) return resp, err } // GenerateNewAddress generates a new deposit address for a currency -func (p *HitBTC) GenerateNewAddress(currency string) (DepositCryptoAddresses, error) { +func (h *HitBTC) GenerateNewAddress(currency string) (DepositCryptoAddresses, error) { resp := DepositCryptoAddresses{} - err := p.SendAuthenticatedHTTPRequest("POST", apiV2CryptoAddress+"/"+currency, url.Values{}, &resp) + err := h.SendAuthenticatedHTTPRequest("POST", apiV2CryptoAddress+"/"+currency, url.Values{}, &resp) return resp, err } // GetActiveorders returns all your active orders -func (p *HitBTC) GetActiveorders(currency string) ([]Order, error) { +func (h *HitBTC) GetActiveorders(currency string) ([]Order, error) { resp := []Order{} - err := p.SendAuthenticatedHTTPRequest("GET", orders+"?symbol="+currency, url.Values{}, &resp) + err := h.SendAuthenticatedHTTPRequest("GET", orders+"?symbol="+currency, url.Values{}, &resp) return resp, err } // GetAuthenticatedTradeHistory returns your trade history -func (p *HitBTC) GetAuthenticatedTradeHistory(currency, start, end string) (interface{}, error) { +func (h *HitBTC) GetAuthenticatedTradeHistory(currency, start, end string) (interface{}, error) { values := url.Values{} if start != "" { @@ -394,17 +402,17 @@ func (p *HitBTC) GetAuthenticatedTradeHistory(currency, start, end string) (inte values.Set("currencyPair", currency) result := AuthenticatedTradeHistoryResponse{} - return result, p.SendAuthenticatedHTTPRequest("POST", apiV2TradeHistory, values, &result.Data) + return result, h.SendAuthenticatedHTTPRequest("POST", apiV2TradeHistory, values, &result.Data) } values.Set("currencyPair", "all") result := AuthenticatedTradeHistoryAll{} - return result, p.SendAuthenticatedHTTPRequest("POST", apiV2TradeHistory, values, &result.Data) + return result, h.SendAuthenticatedHTTPRequest("POST", apiV2TradeHistory, values, &result.Data) } // PlaceOrder places an order on the exchange -func (p *HitBTC) PlaceOrder(currency string, rate, amount float64, immediate, fillOrKill, buy bool) (OrderResponse, error) { +func (h *HitBTC) PlaceOrder(currency string, rate, amount float64, immediate, fillOrKill, buy bool) (OrderResponse, error) { result := OrderResponse{} values := url.Values{} @@ -427,7 +435,7 @@ func (p *HitBTC) PlaceOrder(currency string, rate, amount float64, immediate, fi values.Set("fillOrKill", "1") } - err := p.SendAuthenticatedHTTPRequest("POST", orderType, values, &result) + err := h.SendAuthenticatedHTTPRequest("POST", orderType, values, &result) if err != nil { return result, err @@ -437,12 +445,12 @@ func (p *HitBTC) PlaceOrder(currency string, rate, amount float64, immediate, fi } // CancelOrder cancels a specific order by OrderID -func (p *HitBTC) CancelOrder(orderID int64) (bool, error) { +func (h *HitBTC) CancelOrder(orderID int64) (bool, error) { result := GenericResponse{} values := url.Values{} values.Set("orderNumber", strconv.FormatInt(orderID, 10)) - err := p.SendAuthenticatedHTTPRequest("POST", orderCancel, values, &result) + err := h.SendAuthenticatedHTTPRequest("POST", orderCancel, values, &result) if err != nil { return false, err @@ -456,7 +464,7 @@ func (p *HitBTC) CancelOrder(orderID int64) (bool, error) { } // MoveOrder generates a new move order -func (p *HitBTC) MoveOrder(orderID int64, rate, amount float64) (MoveOrderResponse, error) { +func (h *HitBTC) MoveOrder(orderID int64, rate, amount float64) (MoveOrderResponse, error) { result := MoveOrderResponse{} values := url.Values{} values.Set("orderNumber", strconv.FormatInt(orderID, 10)) @@ -466,7 +474,7 @@ func (p *HitBTC) MoveOrder(orderID int64, rate, amount float64) (MoveOrderRespon values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64)) } - err := p.SendAuthenticatedHTTPRequest("POST", orderMove, values, &result) + err := h.SendAuthenticatedHTTPRequest("POST", orderMove, values, &result) if err != nil { return result, err @@ -480,7 +488,7 @@ func (p *HitBTC) MoveOrder(orderID int64, rate, amount float64) (MoveOrderRespon } // Withdraw allows for the withdrawal to a specific address -func (p *HitBTC) Withdraw(currency, address string, amount float64) (bool, error) { +func (h *HitBTC) Withdraw(currency, address string, amount float64) (bool, error) { result := Withdraw{} values := url.Values{} @@ -488,7 +496,7 @@ func (p *HitBTC) Withdraw(currency, address string, amount float64) (bool, error values.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64)) values.Set("address", address) - err := p.SendAuthenticatedHTTPRequest("POST", apiV2CryptoWithdraw, values, &result) + err := h.SendAuthenticatedHTTPRequest("POST", apiV2CryptoWithdraw, values, &result) if err != nil { return false, err @@ -502,21 +510,21 @@ func (p *HitBTC) Withdraw(currency, address string, amount float64) (bool, error } // GetFeeInfo returns current fee information -func (p *HitBTC) GetFeeInfo(currencyPair string) (Fee, error) { +func (h *HitBTC) GetFeeInfo(currencyPair string) (Fee, error) { result := Fee{} - err := p.SendAuthenticatedHTTPRequest("GET", apiV2FeeInfo+"/"+currencyPair, url.Values{}, &result) + err := h.SendAuthenticatedHTTPRequest("GET", apiV2FeeInfo+"/"+currencyPair, url.Values{}, &result) return result, err } // GetTradableBalances returns current tradable balances -func (p *HitBTC) GetTradableBalances() (map[string]map[string]float64, error) { +func (h *HitBTC) GetTradableBalances() (map[string]map[string]float64, error) { type Response struct { Data map[string]map[string]interface{} } result := Response{} - err := p.SendAuthenticatedHTTPRequest("POST", tradableBalances, url.Values{}, &result.Data) + err := h.SendAuthenticatedHTTPRequest("POST", tradableBalances, url.Values{}, &result.Data) if err != nil { return nil, err @@ -535,7 +543,7 @@ func (p *HitBTC) GetTradableBalances() (map[string]map[string]float64, error) { } // TransferBalance transfers a balance -func (p *HitBTC) TransferBalance(currency, from, to string, amount float64) (bool, error) { +func (h *HitBTC) TransferBalance(currency, from, to string, amount float64) (bool, error) { values := url.Values{} result := GenericResponse{} @@ -544,7 +552,7 @@ func (p *HitBTC) TransferBalance(currency, from, to string, amount float64) (boo values.Set("fromAccount", from) values.Set("toAccount", to) - err := p.SendAuthenticatedHTTPRequest("POST", transferBalance, values, &result) + err := h.SendAuthenticatedHTTPRequest("POST", transferBalance, values, &result) if err != nil { return false, err @@ -558,19 +566,64 @@ func (p *HitBTC) TransferBalance(currency, from, to string, amount float64) (boo } // SendHTTPRequest sends an unauthenticated HTTP request -func (p *HitBTC) SendHTTPRequest(path string, result interface{}) error { - return p.SendPayload("GET", path, nil, nil, result, false, p.Verbose) +func (h *HitBTC) SendHTTPRequest(path string, result interface{}) error { + return h.SendPayload("GET", path, nil, nil, result, false, h.Verbose) } // SendAuthenticatedHTTPRequest sends an authenticated http request -func (p *HitBTC) SendAuthenticatedHTTPRequest(method, endpoint string, values url.Values, result interface{}) error { - if !p.AuthenticatedAPISupport { - return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, p.Name) +func (h *HitBTC) SendAuthenticatedHTTPRequest(method, endpoint string, values url.Values, result interface{}) error { + if !h.AuthenticatedAPISupport { + return fmt.Errorf(exchange.WarningAuthenticatedRequestWithoutCredentialsSet, h.Name) } headers := make(map[string]string) - headers["Authorization"] = "Basic " + common.Base64Encode([]byte(p.APIKey+":"+p.APISecret)) + headers["Authorization"] = "Basic " + common.Base64Encode([]byte(h.APIKey+":"+h.APISecret)) - path := fmt.Sprintf("%s/%s", p.APIUrl, endpoint) + path := fmt.Sprintf("%s/%s", h.APIUrl, endpoint) - return p.SendPayload(method, path, headers, bytes.NewBufferString(values.Encode()), result, true, p.Verbose) + return h.SendPayload(method, path, headers, bytes.NewBufferString(values.Encode()), result, true, h.Verbose) +} + +// GetFee returns an estimate of fee based on type of transaction +func (h *HitBTC) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + feeInfo, err := h.GetFeeInfo(feeBuilder.FirstCurrency + feeBuilder.Delimiter + feeBuilder.SecondCurrency) + if err != nil { + return 0, err + } + fee = calculateTradingFee(feeInfo, feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + case exchange.CryptocurrencyWithdrawalFee: + currencyInfo, err := h.GetCurrency(feeBuilder.FirstCurrency) + if err != nil { + return 0, err + } + fee, err = strconv.ParseFloat(currencyInfo.PayoutFee, 64) + if err != nil { + return 0, err + } + case exchange.CyptocurrencyDepositFee: + fee = calculateCryptocurrencyDepositFee(feeBuilder.FirstCurrency, feeBuilder.Amount) + } + + return fee, nil +} + +func calculateCryptocurrencyDepositFee(currency string, amount float64) float64 { + var fee float64 + if currency == symbol.BTC { + fee = 0.0006 + } + return fee * amount +} + +func calculateTradingFee(feeInfo Fee, purchasePrice, amount float64, isMaker bool) float64 { + var volumeFee float64 + if isMaker { + volumeFee = feeInfo.ProvideLiquidityRate + } else { + volumeFee = feeInfo.TakeLiquidityRate + } + + return volumeFee * amount * purchasePrice } diff --git a/exchanges/hitbtc/hitbtc_test.go b/exchanges/hitbtc/hitbtc_test.go index faabff04..873baf8e 100644 --- a/exchanges/hitbtc/hitbtc_test.go +++ b/exchanges/hitbtc/hitbtc_test.go @@ -4,9 +4,11 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) -var p HitBTC +var h HitBTC // Please supply your own APIKEYS here for due diligence testing @@ -16,7 +18,7 @@ const ( ) func TestSetDefaults(t *testing.T) { - p.SetDefaults() + h.SetDefaults() } func TestSetup(t *testing.T) { @@ -31,39 +33,130 @@ func TestSetup(t *testing.T) { hitbtcConfig.APIKey = apiKey hitbtcConfig.APISecret = apiSecret - p.Setup(hitbtcConfig) -} - -func TestGetFee(t *testing.T) { - if p.GetFee() != 0 { - t.Error("Test faild - HitBTC GetFee() error") - } + h.Setup(hitbtcConfig) } func TestGetOrderbook(t *testing.T) { - _, err := p.GetOrderbook("BTCUSD", 50) + _, err := h.GetOrderbook("BTCUSD", 50) if err != nil { t.Error("Test faild - HitBTC GetOrderbook() error", err) } } func TestGetTrades(t *testing.T) { - _, err := p.GetTrades("BTCUSD", "", "", "", "", "", "") + _, err := h.GetTrades("BTCUSD", "", "", "", "", "", "") if err != nil { t.Error("Test faild - HitBTC GetTradeHistory() error", err) } } func TestGetChartCandles(t *testing.T) { - _, err := p.GetCandles("BTCUSD", "", "") + _, err := h.GetCandles("BTCUSD", "", "") if err != nil { t.Error("Test faild - HitBTC GetChartData() error", err) } } func TestGetCurrencies(t *testing.T) { - _, err := p.GetCurrencies("") + _, err := h.GetCurrencies() if err != nil { t.Error("Test faild - HitBTC GetCurrencies() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.ETH, + SecondCurrency: symbol.BTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + h.SetDefaults() + TestSetup(t) + + var feeBuilder = setFeeBuilder() + if apiKey != "" && apiSecret != "" { + // CryptocurrencyTradeFee Basic + if resp, err := h.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := h.GetFee(feeBuilder); resp != float64(1000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(1000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := h.GetFee(feeBuilder); resp != float64(-0.0001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(-0.0001), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := h.GetFee(feeBuilder); resp != float64(-1) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(-1), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := h.GetFee(feeBuilder); resp != float64(0.009580) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.009580), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err == nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + feeBuilder.FirstCurrency = symbol.BTC + feeBuilder.SecondCurrency = symbol.LTC + if resp, err := h.GetFee(feeBuilder); resp != float64(0.0006) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0006), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/hitbtc/hitbtc_types.go b/exchanges/hitbtc/hitbtc_types.go index 335a3ffe..fb998b89 100644 --- a/exchanges/hitbtc/hitbtc_types.go +++ b/exchanges/hitbtc/hitbtc_types.go @@ -91,6 +91,8 @@ type Currencies struct { PayoutEnabled bool `json:"payoutEnabled"` // Is allowed for withdraw (false for ICO) PayoutIsPaymentID bool `json:"payoutIsPaymentId"` // Is allowed to provide additional information for withdraw TransferEnabled bool `json:"transferEnabled"` // Is allowed to transfer between trading and account (may be disabled on maintain) + Delisted bool `json:"delisted"` // True if currency delisted (stopped deposit and trading) + PayoutFee string `json:"payoutFee"` // Default withdraw fee } // LoanOrder contains information about your loans diff --git a/exchanges/hitbtc/hitbtc_wrapper.go b/exchanges/hitbtc/hitbtc_wrapper.go index 64f385b6..094afe5f 100644 --- a/exchanges/hitbtc/hitbtc_wrapper.go +++ b/exchanges/hitbtc/hitbtc_wrapper.go @@ -208,3 +208,8 @@ func (h *HitBTC) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curr func (h *HitBTC) GetWebsocket() (*exchange.Websocket, error) { return h.Websocket, nil } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (h *HitBTC) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return h.GetFee(feeBuilder) +} diff --git a/exchanges/huobi/huobi.go b/exchanges/huobi/huobi.go index b7f3ca90..db544a57 100644 --- a/exchanges/huobi/huobi.go +++ b/exchanges/huobi/huobi.go @@ -138,11 +138,6 @@ func (h *HUOBI) Setup(exch config.ExchangeConfig) { } } -// GetFee returns Huobi fee -func (h *HUOBI) GetFee() float64 { - return h.Fee -} - // GetSpotKline returns kline data // KlinesRequestParams contains symbol, period and size func (h *HUOBI) GetSpotKline(arg KlinesRequestParams) ([]KlineItem, error) { @@ -853,3 +848,22 @@ func (h *HUOBI) SendAuthenticatedHTTPRequest(method, endpoint string, values url return h.SendPayload(method, url, headers, bytes.NewReader(body), result, true, h.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (h *HUOBI) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount) + } + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(purchasePrice, amount float64) float64 { + feePercent := 0.002 + return feePercent * purchasePrice * amount +} diff --git a/exchanges/huobi/huobi_test.go b/exchanges/huobi/huobi_test.go index 26cc27cc..b31a9e39 100644 --- a/exchanges/huobi/huobi_test.go +++ b/exchanges/huobi/huobi_test.go @@ -12,6 +12,8 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) // Please supply you own test keys here for due diligence testing. @@ -70,13 +72,6 @@ func TestSetup(t *testing.T) { h.Setup(hConfig) } -func TestGetFee(t *testing.T) { - t.Parallel() - if h.GetFee() != 0 { - t.Errorf("test failed - Huobi GetFee() error") - } -} - func TestGetSpotKline(t *testing.T) { t.Parallel() _, err := h.GetSpotKline(KlinesRequestParams{ @@ -296,3 +291,93 @@ func TestPEMLoadAndSign(t *testing.T) { t.Fatalf("Test Failed. TestPEMLoadAndSign Unable to sign: %s", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "_", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + t.Parallel() + var feeBuilder = setFeeBuilder() + // CryptocurrencyTradeFee Basic + if resp, err := h.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := h.GetFee(feeBuilder); resp != float64(2000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := h.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/huobi/huobi_wrapper.go b/exchanges/huobi/huobi_wrapper.go index e875df5c..f4fbbd42 100644 --- a/exchanges/huobi/huobi_wrapper.go +++ b/exchanges/huobi/huobi_wrapper.go @@ -223,3 +223,8 @@ func (h *HUOBI) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curre func (h *HUOBI) GetWebsocket() (*exchange.Websocket, error) { return h.Websocket, nil } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (h *HUOBI) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return h.GetFee(feeBuilder) +} diff --git a/exchanges/huobihadax/huobihadax.go b/exchanges/huobihadax/huobihadax.go index 911eb42c..7883920d 100644 --- a/exchanges/huobihadax/huobihadax.go +++ b/exchanges/huobihadax/huobihadax.go @@ -121,11 +121,6 @@ func (h *HUOBIHADAX) Setup(exch config.ExchangeConfig) { } } -// GetFee returns Huobi fee -func (h *HUOBIHADAX) GetFee() float64 { - return h.Fee -} - // GetSpotKline returns kline data // KlinesRequestParams holds the Kline request params func (h *HUOBIHADAX) GetSpotKline(arg KlinesRequestParams) ([]KlineItem, error) { @@ -809,3 +804,22 @@ func (h *HUOBIHADAX) SendAuthenticatedHTTPRequest(method, endpoint string, value return h.SendPayload(method, url, headers, bytes.NewBufferString(""), result, true, h.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (h *HUOBIHADAX) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount) + } + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(purchasePrice, amount float64) float64 { + feePercent := 0.002 + return feePercent * purchasePrice * amount +} diff --git a/exchanges/huobihadax/huobihadax_test.go b/exchanges/huobihadax/huobihadax_test.go index 7abf862d..e1f920ed 100644 --- a/exchanges/huobihadax/huobihadax_test.go +++ b/exchanges/huobihadax/huobihadax_test.go @@ -6,6 +6,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) // Please supply your own APIKEYS here for due diligence testing @@ -65,13 +67,6 @@ func TestSetup(t *testing.T) { h.Setup(hadaxConfig) } -func TestGetFee(t *testing.T) { - t.Parallel() - if h.GetFee() != 0 { - t.Errorf("test failed - Huobi GetFee() error") - } -} - func TestGetSpotKline(t *testing.T) { t.Parallel() _, err := h.GetSpotKline(KlinesRequestParams{ @@ -275,3 +270,93 @@ func TestCancelWithdraw(t *testing.T) { t.Error("Test failed - Huobi TestCancelWithdraw: Invalid withdraw-ID was valid") } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "_", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + t.Parallel() + var feeBuilder = setFeeBuilder() + // CryptocurrencyTradeFee Basic + if resp, err := h.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := h.GetFee(feeBuilder); resp != float64(2000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := h.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := h.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/huobihadax/huobihadax_wrapper.go b/exchanges/huobihadax/huobihadax_wrapper.go index 68bc2eb0..5d1e63d4 100644 --- a/exchanges/huobihadax/huobihadax_wrapper.go +++ b/exchanges/huobihadax/huobihadax_wrapper.go @@ -188,3 +188,8 @@ func (h *HUOBIHADAX) WithdrawFiatExchangeFundsToInternationalBank(currency pair. func (h *HUOBIHADAX) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (h *HUOBIHADAX) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return h.GetFee(feeBuilder) +} diff --git a/exchanges/itbit/itbit.go b/exchanges/itbit/itbit.go index b394a992..f3753bbe 100644 --- a/exchanges/itbit/itbit.go +++ b/exchanges/itbit/itbit.go @@ -101,14 +101,6 @@ func (i *ItBit) Setup(exch config.ExchangeConfig) { } } -// GetFee returns the maker or taker fee -func (i *ItBit) GetFee(maker bool) float64 { - if maker { - return i.MakerFee - } - return i.TakerFee -} - // GetTicker returns ticker info for a specified market. // currencyPair - example "XBTUSD" "XBTSGD" "XBTEUR" func (i *ItBit) GetTicker(currencyPair string) (Ticker, error) { @@ -376,3 +368,27 @@ func (i *ItBit) SendAuthenticatedHTTPRequest(method string, path string, params return i.SendPayload(method, url, headers, bytes.NewBuffer([]byte(PayloadJSON)), result, true, i.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (i *ItBit) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + } + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(purchasePrice, amount float64, isMaker bool) float64 { + // TODO: Itbit has volume discounts, but not API endpoint to get the exact volume numbers + // When support is added, this needs to be updated to calculate the accurate volume fee + feePercent := 0.0025 + if isMaker { + feePercent = 0 + } + return feePercent * purchasePrice * amount +} diff --git a/exchanges/itbit/itbit_test.go b/exchanges/itbit/itbit_test.go index 2ae18ab7..36468ad3 100644 --- a/exchanges/itbit/itbit_test.go +++ b/exchanges/itbit/itbit_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var i ItBit @@ -36,13 +38,6 @@ func TestSetup(t *testing.T) { i.Setup(itbitConfig) } -func TestGetFee(t *testing.T) { - t.Parallel() - if i.GetFee(true) != -0.1 || i.GetFee(false) != 0.5 { - t.Error("Test Failed - GetFee() error") - } -} - func TestGetTicker(t *testing.T) { t.Parallel() _, err := i.GetTicker("XBTUSD") @@ -144,3 +139,93 @@ func TestWalletTransfer(t *testing.T) { t.Error("Test Failed - WalletTransfer() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "_", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + t.Parallel() + var feeBuilder = setFeeBuilder() + // CryptocurrencyTradeFee Basic + if resp, err := i.GetFee(feeBuilder); resp != float64(0.0025) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := i.GetFee(feeBuilder); resp != float64(2500) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2500), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := i.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := i.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := i.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := i.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := i.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := i.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := i.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/itbit/itbit_wrapper.go b/exchanges/itbit/itbit_wrapper.go index eda3a346..5752ffac 100644 --- a/exchanges/itbit/itbit_wrapper.go +++ b/exchanges/itbit/itbit_wrapper.go @@ -182,3 +182,8 @@ func (i *ItBit) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curre func (i *ItBit) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (i *ItBit) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return i.GetFee(feeBuilder) +} diff --git a/exchanges/kraken/kraken.go b/exchanges/kraken/kraken.go index 624abbc7..1a34759f 100644 --- a/exchanges/kraken/kraken.go +++ b/exchanges/kraken/kraken.go @@ -16,29 +16,31 @@ import ( ) const ( - krakenAPIURL = "https://api.kraken.com" - krakenAPIVersion = "0" - krakenServerTime = "Time" - krakenAssets = "Assets" - krakenAssetPairs = "AssetPairs" - krakenTicker = "Ticker" - krakenOHLC = "OHLC" - krakenDepth = "Depth" - krakenTrades = "Trades" - krakenSpread = "Spread" - krakenBalance = "Balance" - krakenTradeBalance = "TradeBalance" - krakenOpenOrders = "OpenOrders" - krakenClosedOrders = "ClosedOrders" - krakenQueryOrders = "QueryOrders" - krakenTradeHistory = "TradesHistory" - krakenQueryTrades = "QueryTrades" - krakenOpenPositions = "OpenPositions" - krakenLedgers = "Ledgers" - krakenQueryLedgers = "QueryLedgers" - krakenTradeVolume = "TradeVolume" - krakenOrderCancel = "CancelOrder" - krakenOrderPlace = "AddOrder" + krakenAPIURL = "https://api.kraken.com" + krakenAPIVersion = "0" + krakenServerTime = "Time" + krakenAssets = "Assets" + krakenAssetPairs = "AssetPairs" + krakenTicker = "Ticker" + krakenOHLC = "OHLC" + krakenDepth = "Depth" + krakenTrades = "Trades" + krakenSpread = "Spread" + krakenBalance = "Balance" + krakenTradeBalance = "TradeBalance" + krakenOpenOrders = "OpenOrders" + krakenClosedOrders = "ClosedOrders" + krakenQueryOrders = "QueryOrders" + krakenTradeHistory = "TradesHistory" + krakenQueryTrades = "QueryTrades" + krakenOpenPositions = "OpenPositions" + krakenLedgers = "Ledgers" + krakenQueryLedgers = "QueryLedgers" + krakenTradeVolume = "TradeVolume" + krakenOrderCancel = "CancelOrder" + krakenOrderPlace = "AddOrder" + krakenWithdrawInfo = "WithdrawInfo" + krakenDepositMethods = "DepositMethods" krakenAuthRate = 0 krakenUnauthRate = 0 @@ -115,14 +117,6 @@ func (k *Kraken) Setup(exch config.ExchangeConfig) { } } -// GetFee returns current fee for either crypto or fiat -func (k *Kraken) GetFee(cryptoTrade bool) float64 { - if cryptoTrade { - return k.CryptoFee - } - return k.FiatFee -} - // GetServerTime returns current server time func (k *Kraken) GetServerTime() (TimeResponse, error) { path := fmt.Sprintf("%s/%s/public/%s", k.APIUrl, krakenAPIVersion, krakenServerTime) @@ -416,6 +410,40 @@ func (k *Kraken) GetBalance() (map[string]float64, error) { return result, GetError(response.Error) } +// GetWithdrawInfo gets withdrawal fees +func (k *Kraken) GetWithdrawInfo(currency string, amount float64) (WithdrawInformation, error) { + var response struct { + Error []string `json:"error"` + Result WithdrawInformation `json:"result"` + } + params := url.Values{} + params.Set("asset ", currency) + params.Set("key ", "") + params.Set("amount ", fmt.Sprintf("%f", amount)) + + if err := k.SendAuthenticatedHTTPRequest(krakenWithdrawInfo, params, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) +} + +// GetDepositMethods gets withdrawal fees +func (k *Kraken) GetDepositMethods(currency string) ([]DepositMethods, error) { + var response struct { + Error []string `json:"error"` + Result []DepositMethods `json:"result"` + } + params := url.Values{} + params.Set("asset ", currency) + + if err := k.SendAuthenticatedHTTPRequest(krakenDepositMethods, params, &response); err != nil { + return response.Result, err + } + + return response.Result, GetError(response.Error) +} + // GetTradeBalance returns full information about your trades on Kraken func (k *Kraken) GetTradeBalance(args ...TradeBalanceOptions) (TradeBalanceInfo, error) { params := url.Values{} @@ -852,3 +880,61 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(method string, params url.Values, return k.SendPayload("POST", k.APIUrl+path, headers, strings.NewReader(encoded), result, true, k.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (k *Kraken) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + currency := feeBuilder.FirstCurrency + feeBuilder.Delimiter + feeBuilder.SecondCurrency + + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + feePair, err := k.GetTradeVolume(true, currency) + if err != nil { + return 0, err + } + if feeBuilder.IsMaker { + fee = calculateTradingFee(currency, feePair.FeesMaker, feeBuilder.PurchasePrice, feeBuilder.Amount) + } else { + fee = calculateTradingFee(currency, feePair.Fees, feeBuilder.PurchasePrice, feeBuilder.Amount) + } + case exchange.CryptocurrencyWithdrawalFee: + fee = getWithdrawalFee(feeBuilder.FirstCurrency) + case exchange.InternationalBankDepositFee: + depositMethods, err := k.GetDepositMethods(feeBuilder.CurrencyItem) + if err != nil { + return 0, err + } + + for _, i := range depositMethods { + switch feeBuilder.BankTransactionType { + case exchange.WireTransfer: + if i.Method == "SynapsePay (US Wire)" { + fee = i.Fee + return fee, nil + } + } + } + case exchange.CyptocurrencyDepositFee: + fee = getCryptocurrencyDepositFee(feeBuilder.FirstCurrency) + + case exchange.InternationalBankWithdrawalFee: + fee = getWithdrawalFee(feeBuilder.CurrencyItem) + } + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func getWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} + +func getCryptocurrencyDepositFee(currency string) float64 { + return DepositFees[currency] +} + +func calculateTradingFee(currency string, feePair map[string]TradeVolumeFee, purchasePrice, amount float64) float64 { + return (feePair[currency].Fee / 100) * purchasePrice * amount +} diff --git a/exchanges/kraken/kraken_test.go b/exchanges/kraken/kraken_test.go index eb3c1cae..91badeba 100644 --- a/exchanges/kraken/kraken_test.go +++ b/exchanges/kraken/kraken_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var k Kraken @@ -35,16 +37,6 @@ func TestSetup(t *testing.T) { k.Setup(krakenConfig) } -func TestGetFee(t *testing.T) { - t.Parallel() - if k.GetFee(true) != 0.1 { - t.Error("Test Failed - kraken GetFee() error") - } - if k.GetFee(false) != 0.35 { - t.Error("Test Failed - kraken GetFee() error") - } -} - func TestGetServerTime(t *testing.T) { t.Parallel() _, err := k.GetServerTime() @@ -219,3 +211,99 @@ func TestCancelOrder(t *testing.T) { t.Error("Test Failed - CancelOrder() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.XXBT, + SecondCurrency: symbol.ZUSD, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + k.SetDefaults() + TestSetup(t) + var feeBuilder = setFeeBuilder() + + if apiKey != "" && apiSecret != "" { + // CryptocurrencyTradeFee Basic + if resp, err := k.GetFee(feeBuilder); resp != float64(0.0026) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0026), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := k.GetFee(feeBuilder); resp != float64(2600) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2600), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := k.GetFee(feeBuilder); resp != float64(0.0016) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0016), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := k.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := k.GetFee(feeBuilder); resp != float64(5) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(5), resp) + t.Error(err) + } + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + feeBuilder.FirstCurrency = symbol.XXBT + if resp, err := k.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(5), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := k.GetFee(feeBuilder); resp != float64(0.0005) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0005), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := k.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := k.GetFee(feeBuilder); resp != float64(5) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(5), resp) + t.Error(err) + } +} diff --git a/exchanges/kraken/kraken_types.go b/exchanges/kraken/kraken_types.go index 764aa28e..7bf4ed95 100644 --- a/exchanges/kraken/kraken_types.go +++ b/exchanges/kraken/kraken_types.go @@ -1,5 +1,7 @@ package kraken +import "github.com/thrasher-/gocryptotrader/currency/symbol" + // TimeResponse type type TimeResponse struct { Unixtime int64 `json:"unixtime"` @@ -285,6 +287,21 @@ type AddOrderResponse struct { TransactionIds []string `json:"txid"` } +// WithdrawInformation Used to check withdrawal fees +type WithdrawInformation struct { + Method string `json:"method"` + Limit float64 `json:"limit,string"` + Fee float64 `json:"fee,string"` +} + +// DepositMethods Used to check deposit fees +type DepositMethods struct { + Method string `json:"method"` + Limit float64 `json:"limit,string"` + Fee float64 `json:"fee,string"` + AddressSetupFee float64 `json:"address-setup-fee,string"` +} + // OrderDescription represents an orders description type OrderDescription struct { Close string `json:"close"` @@ -308,3 +325,40 @@ type CancelOrderResponse struct { Count int64 `json:"count"` Pending interface{} `json:"pending"` } + +// DepositFees the large list of predefined deposit fees +// Prone to change +var DepositFees = map[string]float64{ + symbol.XTZ: 0.05, +} + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change +var WithdrawalFees = map[string]float64{ + symbol.ZUSD: 5, + symbol.ZEUR: 5, + symbol.USD: 5, + symbol.EUR: 5, + symbol.REP: 0.01, + symbol.XXBT: 0.0005, + symbol.BTC: 0.0005, + symbol.XBT: 0.0005, + symbol.BCH: 0.0001, + symbol.ADA: 0.3, + symbol.DASH: 0.005, + symbol.XDG: 2, + symbol.EOS: 0.05, + symbol.ETH: 0.005, + symbol.ETC: 0.005, + symbol.GNO: 0.005, + symbol.ICN: 0.2, + symbol.LTC: 0.001, + symbol.MLN: 0.003, + symbol.XMR: 0.05, + symbol.QTUM: 0.01, + symbol.XRP: 0.02, + symbol.XLM: 0.00002, + symbol.USDT: 5, + symbol.XTZ: 0.05, + symbol.ZEC: 0.0001, +} diff --git a/exchanges/kraken/kraken_wrapper.go b/exchanges/kraken/kraken_wrapper.go index 402f0ced..73984c74 100644 --- a/exchanges/kraken/kraken_wrapper.go +++ b/exchanges/kraken/kraken_wrapper.go @@ -255,3 +255,8 @@ func (k *Kraken) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curr func (k *Kraken) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (k *Kraken) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return k.GetFee(feeBuilder) +} diff --git a/exchanges/lakebtc/lakebtc.go b/exchanges/lakebtc/lakebtc.go index 552b9b0e..3afcbe59 100644 --- a/exchanges/lakebtc/lakebtc.go +++ b/exchanges/lakebtc/lakebtc.go @@ -10,6 +10,7 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -117,14 +118,6 @@ func (l *LakeBTC) GetTradablePairs() ([]string, error) { return currencies, nil } -// GetFee returns maker or taker fee -func (l *LakeBTC) GetFee(maker bool) float64 { - if maker { - return l.MakerFee - } - return l.TakerFee -} - // GetTicker returns the current ticker from lakeBTC func (l *LakeBTC) GetTicker() (map[string]Ticker, error) { response := make(map[string]TickerResponse) @@ -352,3 +345,38 @@ func (l *LakeBTC) SendAuthenticatedHTTPRequest(method, params string, result int return l.SendPayload("POST", l.APIUrl, headers, strings.NewReader(string(data)), result, true, l.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (l *LakeBTC) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + case exchange.CyptocurrencyDepositFee: + fee = getCryptocurrencyWithdrawalFee(feeBuilder.FirstCurrency) + } + + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(purchasePrice, amount float64, isMaker bool) (fee float64) { + if isMaker { + // TODO: Volume based fee calculation + fee = 0.0015 + } else { + fee = 0.002 + } + + return fee * amount * purchasePrice +} + +func getCryptocurrencyWithdrawalFee(currency string) (fee float64) { + if currency == symbol.BTC { + fee = 0.001 + } + return fee +} diff --git a/exchanges/lakebtc/lakebtc_test.go b/exchanges/lakebtc/lakebtc_test.go index afb77945..bcdbacd8 100644 --- a/exchanges/lakebtc/lakebtc_test.go +++ b/exchanges/lakebtc/lakebtc_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var l LakeBTC @@ -41,16 +43,6 @@ func TestGetTradablePairs(t *testing.T) { } } -func TestGetFee(t *testing.T) { - t.Parallel() - if l.GetFee(false) != 0.2 { - t.Error("Test Failed - GetFee() error") - } - if l.GetFee(true) != 0.15 { - t.Error("Test Failed - GetFee() error") - } -} - func TestGetTicker(t *testing.T) { t.Parallel() _, err := l.GetTicker() @@ -151,3 +143,93 @@ func TestCreateWithdraw(t *testing.T) { t.Error("Test Failed - CreateWithdraw() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "_", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.BTC, + SecondCurrency: symbol.LTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + t.Parallel() + var feeBuilder = setFeeBuilder() + // CryptocurrencyTradeFee Basic + if resp, err := l.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := l.GetFee(feeBuilder); resp != float64(2000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := l.GetFee(feeBuilder); resp != float64(0.0015) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0015), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/lakebtc/lakebtc_wrapper.go b/exchanges/lakebtc/lakebtc_wrapper.go index 45859d49..90c86100 100644 --- a/exchanges/lakebtc/lakebtc_wrapper.go +++ b/exchanges/lakebtc/lakebtc_wrapper.go @@ -192,3 +192,8 @@ func (l *LakeBTC) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Cur func (l *LakeBTC) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (l *LakeBTC) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return l.GetFee(feeBuilder) +} diff --git a/exchanges/liqui/liqui.go b/exchanges/liqui/liqui.go index d764ee64..99f85a28 100644 --- a/exchanges/liqui/liqui.go +++ b/exchanges/liqui/liqui.go @@ -1,7 +1,6 @@ package liqui import ( - "errors" "fmt" "log" "net/url" @@ -109,16 +108,6 @@ func (l *Liqui) Setup(exch config.ExchangeConfig) { } } -// GetFee returns a fee for a specific currency -func (l *Liqui) GetFee(currency string) (float64, error) { - val, ok := l.Info.Pairs[common.StringToLower(currency)] - if !ok { - return 0, errors.New("currency does not exist") - } - - return val.Fee, nil -} - // GetAvailablePairs returns all available pairs func (l *Liqui) GetAvailablePairs(nonHidden bool) []string { var pairs []string @@ -315,3 +304,33 @@ func (l *Liqui) SendAuthenticatedHTTPRequest(method string, values url.Values, r true, l.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (l *Liqui) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + case exchange.CryptocurrencyWithdrawalFee: + fee = getCryptocurrencyWithdrawalFee(feeBuilder.FirstCurrency) + } + + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func getCryptocurrencyWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} + +func calculateTradingFee(purchasePrice, amount float64, isMaker bool) (fee float64) { + if isMaker { + fee = 0.001 + } else { + fee = 0.0025 + } + return fee * purchasePrice * amount +} diff --git a/exchanges/liqui/liqui_test.go b/exchanges/liqui/liqui_test.go index e18d272e..a63f3fd6 100644 --- a/exchanges/liqui/liqui_test.go +++ b/exchanges/liqui/liqui_test.go @@ -6,6 +6,8 @@ import ( "github.com/thrasher-/gocryptotrader/config" "github.com/thrasher-/gocryptotrader/currency/pair" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var l Liqui @@ -34,14 +36,6 @@ func TestSetup(t *testing.T) { l.Setup(liquiConfig) } -func TestGetFee(t *testing.T) { - t.Parallel() - _, err := l.GetFee("usd") - if err == nil { - t.Error("Test Failed - liqui GetFee() error", err) - } -} - func TestGetAvailablePairs(t *testing.T) { t.Parallel() v := l.GetAvailablePairs(false) @@ -136,3 +130,92 @@ func TestUpdateOrderbook(t *testing.T) { t.Error("Test Failed - liqui UpdateOrderbook() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "-", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.LTC, + SecondCurrency: symbol.BTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} +func TestGetFee(t *testing.T) { + l.SetDefaults() + var feeBuilder = setFeeBuilder() + // CryptocurrencyTradeFee Basic + if resp, err := l.GetFee(feeBuilder); resp != float64(0.0025) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0025), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := l.GetFee(feeBuilder); resp != float64(2500) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := l.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0.01) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.01), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/liqui/liqui_types.go b/exchanges/liqui/liqui_types.go index 4adbd0c5..50063627 100644 --- a/exchanges/liqui/liqui_types.go +++ b/exchanges/liqui/liqui_types.go @@ -1,5 +1,7 @@ package liqui +import "github.com/thrasher-/gocryptotrader/currency/symbol" + // Info holds the current pair information as well as server time type Info struct { ServerTime int64 `json:"server_time"` @@ -132,3 +134,92 @@ type WithdrawCoins struct { Success int `json:"success"` Error string `json:"error"` } + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change +var WithdrawalFees = map[string]float64{ + symbol.ZRX: 5, + symbol.ADX: 10, + symbol.AE: 2, + symbol.AION: 5, + symbol.AST: 25, + symbol.ANT: 2, + symbol.REP: 0.15, + symbol.BNT: 1.5, + symbol.BAT: 20, + symbol.BTC: 0.001, + symbol.BCH: 0.007, + symbol.BMC: 7, + symbol.BCAP: 2, + symbol.TIME: 0.5, + symbol.CVC: 15, + symbol.CFI: 100, + symbol.CLN: 100, + symbol.DASH: 0.003, + symbol.MANA: 40, + symbol.DGD: 0.05, + symbol.DNT: 100, + symbol.EDG: 15, + symbol.ENG: 3, + symbol.ENJ: 50, + symbol.EOS: 0.5, + symbol.ETH: 0.01, + symbol.FIRST: 5, + symbol.GNO: 0.1, + symbol.GNT: 15, + symbol.GOLOS: 0.01, + symbol.GBG: 0.01, + symbol.HMQ: 20, + symbol.ICN: 7, + symbol.RLC: 5, + symbol.INCNT: 1, + symbol.IND: 70, + symbol.INS: 7, + symbol.KNC: 4, + symbol.LDC: 1000, + symbol.LTC: 0.01, + symbol.LUN: 0.5, + symbol.GUP: 40, + symbol.MLN: 0.2, + symbol.MGO: 20, + symbol.MCO: 0.7, + symbol.MYST: 20, + symbol.NEU: 7, + symbol.NET: 10, + symbol.OAX: 10, + symbol.OMG: 0.5, + symbol.PTOY: 50, + symbol.PLU: 0.5, + symbol.PRO: 7, + symbol.QTUM: 0.2, + symbol.QRL: 10, + symbol.REN: 100, + symbol.REQ: 50, + symbol.ROUND: 100, + symbol.SALT: 4, + symbol.SAN: 4, + symbol.SNGLS: 80, + symbol.AGI: 50, + symbol.SRN: 30, + symbol.SNM: 25, + symbol.XID: 50, + symbol.SNT: 50, + symbol.STEEM: 0.01, + symbol.SBD: 0.01, + symbol.STORJ: 7, + symbol.STX: 20, + symbol.TAAS: 2, + symbol.PAY: 5, + symbol.USDT: 20, + symbol.TNT: 100, + symbol.TKN: 5, + symbol.TRX: 100, + symbol.VEN: 3, + symbol.VSL: 30, + symbol.WAVES: 0.01, + symbol.WPR: 100, + symbol.TRST: 100, + symbol.WINGS: 20, + symbol.XXX: 0.01, + symbol.XZC: 0.01, +} diff --git a/exchanges/liqui/liqui_wrapper.go b/exchanges/liqui/liqui_wrapper.go index 1a369f86..736e0f1e 100644 --- a/exchanges/liqui/liqui_wrapper.go +++ b/exchanges/liqui/liqui_wrapper.go @@ -201,3 +201,8 @@ func (l *Liqui) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curre func (l *Liqui) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (l *Liqui) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return l.GetFee(feeBuilder) +} diff --git a/exchanges/localbitcoins/localbitcoins.go b/exchanges/localbitcoins/localbitcoins.go index f1a79cd9..39f7eb87 100644 --- a/exchanges/localbitcoins/localbitcoins.go +++ b/exchanges/localbitcoins/localbitcoins.go @@ -166,14 +166,6 @@ func (l *LocalBitcoins) Setup(exch config.ExchangeConfig) { } } -// GetFee returns the fee for maker or taker -func (l *LocalBitcoins) GetFee(maker bool) float64 { - if maker { - return l.MakerFee - } - return l.TakerFee -} - // GetAccountInfo lets you retrieve the public user information on a // LocalBitcoins user. The response contains the same information that is found // on an account's public profile page. @@ -735,3 +727,9 @@ func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(method, path string, params return l.SendPayload(method, l.APIUrl+path, headers, strings.NewReader(encoded), result, true, l.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (l *LocalBitcoins) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + // No fees will be used + return 0, nil +} diff --git a/exchanges/localbitcoins/localbitcoins_test.go b/exchanges/localbitcoins/localbitcoins_test.go index 9890d95d..7241ea8c 100644 --- a/exchanges/localbitcoins/localbitcoins_test.go +++ b/exchanges/localbitcoins/localbitcoins_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var l LocalBitcoins @@ -34,13 +36,6 @@ func TestSetup(t *testing.T) { l.Setup(localbitcoinsConfig) } -func TestGetFee(t *testing.T) { - t.Parallel() - if l.GetFee(false) != 0 || l.GetFee(true) != 0 { - t.Error("Test Failed - GetFee() error") - } -} - func TestGetAccountInfo(t *testing.T) { t.Parallel() if l.APIKey == "" || l.APISecret == "" { @@ -82,3 +77,93 @@ func TestEditAd(t *testing.T) { t.Error("Test Failed - EditAd() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "-", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.LTC, + SecondCurrency: symbol.BTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + l.SetDefaults() + var feeBuilder = setFeeBuilder() + // CryptocurrencyTradeFee Basic + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := l.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/localbitcoins/localbitcoins_wrapper.go b/exchanges/localbitcoins/localbitcoins_wrapper.go index 308d574c..5abe9165 100644 --- a/exchanges/localbitcoins/localbitcoins_wrapper.go +++ b/exchanges/localbitcoins/localbitcoins_wrapper.go @@ -173,3 +173,8 @@ func (l *LocalBitcoins) WithdrawFiatExchangeFundsToInternationalBank(currency pa func (l *LocalBitcoins) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (l *LocalBitcoins) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return l.GetFee(feeBuilder) +} diff --git a/exchanges/okcoin/okcoin.go b/exchanges/okcoin/okcoin.go index 0ee533f9..6b2dde56 100644 --- a/exchanges/okcoin/okcoin.go +++ b/exchanges/okcoin/okcoin.go @@ -9,6 +9,8 @@ import ( "strings" "time" + "github.com/thrasher-/gocryptotrader/currency/symbol" + "github.com/gorilla/websocket" "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" @@ -180,18 +182,6 @@ func (o *OKCoin) Setup(exch config.ExchangeConfig) { } } -// GetFee returns current fees for the exchange -func (o *OKCoin) GetFee(maker bool) float64 { - if o.APIUrl == okcoinAPIURL { - if maker { - return o.MakerFee - } - return o.TakerFee - } - // Chinese exchange does not have any trading fees - return 0 -} - // GetTicker returns the current ticker func (o *OKCoin) GetTicker(symbol string) (Ticker, error) { resp := TickerResponse{} @@ -1027,3 +1017,46 @@ func (o *OKCoin) SetErrorDefaults() { "20028": "No such contract", } } + +// GetFee returns an estimate of fee based on type of transaction +func (o *OKCoin) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + case exchange.InternationalBankWithdrawalFee: + fee = calculateInternationalBankWithdrawalFee(feeBuilder.CurrencyItem, feeBuilder.PurchasePrice, feeBuilder.Amount) + case exchange.CryptocurrencyWithdrawalFee: + fee = getWithdrawalFee(feeBuilder.FirstCurrency) + } + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(purchasePrice, amount float64, isMaker bool) (fee float64) { + // TODO volume based fees + if isMaker { + fee = 0.0005 + } else { + fee = 0.0015 + } + return fee * amount * purchasePrice +} + +func calculateInternationalBankWithdrawalFee(currency string, purchasePrice, amount float64) (fee float64) { + if currency == symbol.USD { + if purchasePrice*amount*0.001 < 15 { + fee = 15 + } else { + fee = purchasePrice * amount * 0.001 + } + } + return fee +} + +func getWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} diff --git a/exchanges/okcoin/okcoin_test.go b/exchanges/okcoin/okcoin_test.go index 5ef3dced..3dea0ae0 100644 --- a/exchanges/okcoin/okcoin_test.go +++ b/exchanges/okcoin/okcoin_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var o OKCoin @@ -33,3 +35,93 @@ func TestSetup(t *testing.T) { o.Setup(okcoinConfig) } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "-", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.LTC, + SecondCurrency: symbol.BTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + o.SetDefaults() + var feeBuilder = setFeeBuilder() + // CryptocurrencyTradeFee Basic + if resp, err := o.GetFee(feeBuilder); resp != float64(0.0015) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0015), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := o.GetFee(feeBuilder); resp != float64(1500) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(1500), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := o.GetFee(feeBuilder); resp != float64(0.0005) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0005), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := o.GetFee(feeBuilder); resp != float64(0.2) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.2), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := o.GetFee(feeBuilder); resp != float64(15) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(15), resp) + t.Error(err) + } +} diff --git a/exchanges/okcoin/okcoin_types.go b/exchanges/okcoin/okcoin_types.go index 8b595d7b..c067b36a 100644 --- a/exchanges/okcoin/okcoin_types.go +++ b/exchanges/okcoin/okcoin_types.go @@ -1,5 +1,7 @@ package okcoin +import "github.com/thrasher-/gocryptotrader/currency/symbol" + // Ticker holds ticker data type Ticker struct { Buy float64 `json:",string"` @@ -415,3 +417,13 @@ type WebsocketTradeOrderResponse struct { OrderID int64 `json:"order_id,string"` Result bool `json:"result,string"` } + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change, using highest value +var WithdrawalFees = map[string]float64{ + symbol.BTC: 0.005, + symbol.LTC: 0.2, + symbol.ETH: 0.01, + symbol.ETC: 0.2, + symbol.BCH: 0.002, +} diff --git a/exchanges/okcoin/okcoin_wrapper.go b/exchanges/okcoin/okcoin_wrapper.go index 255b6039..9fd49eb0 100644 --- a/exchanges/okcoin/okcoin_wrapper.go +++ b/exchanges/okcoin/okcoin_wrapper.go @@ -239,3 +239,8 @@ func (o *OKCoin) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curr func (o *OKCoin) GetWebsocket() (*exchange.Websocket, error) { return o.Websocket, nil } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (o *OKCoin) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return o.GetFee(feeBuilder) +} diff --git a/exchanges/okex/okex.go b/exchanges/okex/okex.go index 8dbd976a..9791363f 100644 --- a/exchanges/okex/okex.go +++ b/exchanges/okex/okex.go @@ -1107,3 +1107,33 @@ func (o *OKEX) CheckType(typeInput string) error { } return nil } + +// GetFee returns an estimate of fee based on type of transaction +func (o *OKEX) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + case exchange.CryptocurrencyWithdrawalFee: + fee = getWithdrawalFee(feeBuilder.FirstCurrency) + } + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(purchasePrice, amount float64, isMaker bool) (fee float64) { + // TODO volume based fees + if isMaker { + fee = 0.001 + } else { + fee = 0.0015 + } + return fee * amount * purchasePrice +} + +func getWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} diff --git a/exchanges/okex/okex_test.go b/exchanges/okex/okex_test.go index 6028c9fc..392855bd 100644 --- a/exchanges/okex/okex_test.go +++ b/exchanges/okex/okex_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var o OKEX @@ -282,3 +284,93 @@ func TestGetUserInfo(t *testing.T) { t.Error("Test failed - okex GetUserInfo() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "-", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.LTC, + SecondCurrency: symbol.BTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + o.SetDefaults() + var feeBuilder = setFeeBuilder() + // CryptocurrencyTradeFee Basic + if resp, err := o.GetFee(feeBuilder); resp != float64(0.0015) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0015), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := o.GetFee(feeBuilder); resp != float64(1500) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(1500), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := o.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := o.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := o.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/okex/okex_types.go b/exchanges/okex/okex_types.go index 0144798c..f2217aad 100644 --- a/exchanges/okex/okex_types.go +++ b/exchanges/okex/okex_types.go @@ -1,6 +1,7 @@ package okex import "encoding/json" +import "github.com/thrasher-/gocryptotrader/currency/symbol" // ContractPrice holds date and ticker price price for contracts. type ContractPrice struct { @@ -261,3 +262,155 @@ var ( TimeIntervalThreeDays = TimeInterval("3day") TimeIntervalWeek = TimeInterval("1week") ) + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change, using highest value +var WithdrawalFees = map[string]float64{ + symbol.ZRX: 10, + symbol.ACE: 2.2, + symbol.ACT: 0.01, + symbol.AAC: 5, + symbol.AE: 1, + symbol.AIDOC: 17, + symbol.AST: 8, + symbol.SOC: 20, + symbol.ABT: 3, + symbol.ARK: 0.1, + symbol.ATL: 1.5, + symbol.AVT: 1, + symbol.BNT: 1, + symbol.BKX: 3, + symbol.BEC: 4, + symbol.BTC: 0.0005, + symbol.BCH: 0.0001, + symbol.BCD: 0.02, + symbol.BTG: 0.001, + symbol.VEE: 100, + symbol.BRD: 1.5, + symbol.CTR: 7, + symbol.LINK: 10, + symbol.CAG: 2, + symbol.CHAT: 10, + symbol.CVC: 10, + symbol.CIC: 150, + symbol.CBT: 10, + symbol.CAN: 3, + symbol.CMT: 10, + symbol.DADI: 10, + symbol.DASH: 0.002, + symbol.DAT: 2, + symbol.MANA: 20, + symbol.DCR: 0.03, + symbol.DPY: 0.8, + symbol.DENT: 100, + symbol.DGD: 0.2, + symbol.DNT: 20, + symbol.EDO: 2, + symbol.DNA: 3, + symbol.ENG: 5, + symbol.ENJ: 20, + symbol.ETH: 0.01, + symbol.ETC: 0.001, + symbol.LEND: 10, + symbol.EVX: 1.5, + symbol.XUC: 5.8, + symbol.FAIR: 15, + symbol.FIRST: 6, + symbol.FUN: 40, + symbol.GTC: 40, + symbol.GNX: 8, + symbol.GTO: 10, + symbol.GSC: 20, + symbol.GNT: 5, + symbol.HMC: 40, + symbol.HOT: 10, + symbol.ICN: 2, + symbol.INS: 2.5, + symbol.INT: 10, + symbol.IOST: 100, + symbol.ITC: 2, + symbol.IPC: 2.5, + symbol.KNC: 2, + symbol.LA: 3, + symbol.LEV: 20, + symbol.LIGHT: 100, + symbol.LSK: 0.4, + symbol.LTC: 0.001, + symbol.LRC: 7, + symbol.MAG: 34, + symbol.MKR: 0.002, + symbol.MTL: 0.5, + symbol.AMM: 5, + symbol.MITH: 20, + symbol.MDA: 2, + symbol.MOF: 5, + symbol.MCO: 0.2, + symbol.MTH: 35, + symbol.NGC: 1.5, + symbol.NANO: 0.2, + symbol.NULS: 2, + symbol.OAX: 6, + symbol.OF: 600, + symbol.OKB: 0, + symbol.MOT: 1.5, + symbol.OMG: 0.1, + symbol.RNT: 13, + symbol.POE: 30, + symbol.PPT: 0.2, + symbol.PST: 10, + symbol.PRA: 4, + symbol.QTUM: 0.01, + symbol.QUN: 20, + symbol.QVT: 10, + symbol.RDN: 0.3, + symbol.READ: 20, + symbol.RCT: 15, + symbol.RFR: 200, + symbol.REF: 0.2, + symbol.REN: 50, + symbol.REQ: 15, + symbol.R: 2, + symbol.RCN: 20, + symbol.XRP: 0.15, + symbol.SALT: 0.5, + symbol.SAN: 1, + symbol.KEY: 50, + symbol.SSC: 8, + symbol.SHOW: 150, + symbol.SC: 200, + symbol.OST: 3, + symbol.SNGLS: 20, + symbol.SMT: 8, + symbol.SNM: 20, + symbol.SPF: 5, + symbol.SNT: 50, + symbol.STORJ: 2, + symbol.SUB: 4, + symbol.SNC: 10, + symbol.SWFTC: 350, + symbol.PAY: 0.5, + symbol.USDT: 2, + symbol.TRA: 500, + symbol.THETA: 20, + symbol.TNB: 40, + symbol.TCT: 50, + symbol.TOPC: 20, + symbol.TIO: 2.5, + symbol.TRIO: 200, + symbol.TRUE: 4, + symbol.UCT: 10, + symbol.UGC: 12, + symbol.UKG: 2.5, + symbol.UTK: 3, + symbol.VIB: 6, + symbol.VIU: 40, + symbol.WTC: 0.4, + symbol.WFEE: 500, + symbol.WRC: 48, + symbol.YEE: 70, + symbol.YOYOW: 10, + symbol.ZEC: 0.001, + symbol.ZEN: 0.07, + symbol.ZIL: 20, + symbol.ZIP: 1000, +} diff --git a/exchanges/okex/okex_wrapper.go b/exchanges/okex/okex_wrapper.go index 7f2cbba2..a756b345 100644 --- a/exchanges/okex/okex_wrapper.go +++ b/exchanges/okex/okex_wrapper.go @@ -205,3 +205,8 @@ func (o *OKEX) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curren func (o *OKEX) GetWebsocket() (*exchange.Websocket, error) { return o.Websocket, nil } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (o *OKEX) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return o.GetFee(feeBuilder) +} diff --git a/exchanges/poloniex/poloniex.go b/exchanges/poloniex/poloniex.go index d7f5ee7c..2cc3f5d6 100644 --- a/exchanges/poloniex/poloniex.go +++ b/exchanges/poloniex/poloniex.go @@ -129,11 +129,6 @@ func (p *Poloniex) Setup(exch config.ExchangeConfig) { } } -// GetFee returns the fee for poloniex -func (p *Poloniex) GetFee() float64 { - return p.Fee -} - // GetTicker returns current ticker information func (p *Poloniex) GetTicker() (map[string]Ticker, error) { type response struct { @@ -891,3 +886,36 @@ func (p *Poloniex) SendAuthenticatedHTTPRequest(method, endpoint string, values return p.SendPayload(method, path, headers, bytes.NewBufferString(values.Encode()), result, true, p.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (p *Poloniex) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + feeInfo, err := p.GetFeeInfo() + if err != nil { + return 0, err + } + fee = calculateTradingFee(feeInfo, feeBuilder.PurchasePrice, feeBuilder.Amount, feeBuilder.IsMaker) + case exchange.CryptocurrencyWithdrawalFee: + fee = getWithdrawalFee(feeBuilder.FirstCurrency) + } + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(feeInfo Fee, purchasePrice, amount float64, isMaker bool) (fee float64) { + if isMaker { + fee = feeInfo.MakerFee + } else { + fee = feeInfo.TakerFee + } + return fee * amount * purchasePrice +} + +func getWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} diff --git a/exchanges/poloniex/poloniex_test.go b/exchanges/poloniex/poloniex_test.go index 7ac8f683..57e97e63 100644 --- a/exchanges/poloniex/poloniex_test.go +++ b/exchanges/poloniex/poloniex_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var p Poloniex @@ -34,12 +36,6 @@ func TestSetup(t *testing.T) { p.Setup(poloniexConfig) } -func TestGetFee(t *testing.T) { - if p.GetFee() != 0 { - t.Error("Test faild - Poloniex GetFee() error") - } -} - func TestGetTicker(t *testing.T) { _, err := p.GetTicker() if err != nil { @@ -88,3 +84,97 @@ func TestGetLoanOrders(t *testing.T) { t.Error("Test faild - Poloniex GetLoanOrders() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "-", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.LTC, + SecondCurrency: symbol.BTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + p.SetDefaults() + TestSetup(t) + var feeBuilder = setFeeBuilder() + + if apiKey != "" && apiSecret != "" { + // CryptocurrencyTradeFee Basic + if resp, err := p.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0015), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := p.GetFee(feeBuilder); resp != float64(2000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := p.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := p.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := p.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/poloniex/poloniex_types.go b/exchanges/poloniex/poloniex_types.go index 484d36b9..a4517e97 100644 --- a/exchanges/poloniex/poloniex_types.go +++ b/exchanges/poloniex/poloniex_types.go @@ -1,5 +1,7 @@ package poloniex +import "github.com/thrasher-/gocryptotrader/currency/symbol" + // Ticker holds ticker data type Ticker struct { Last float64 `json:"last,string"` @@ -225,7 +227,6 @@ type Fee struct { MakerFee float64 `json:"makerFee,string"` TakerFee float64 `json:"takerFee,string"` ThirtyDayVolume float64 `json:"thirtyDayVolume,string"` - NextTier float64 `json:"nextTier,string"` } // Margin holds margin information @@ -334,3 +335,68 @@ type WsTrade struct { Price float64 Timestamp int64 } + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change, using highest value +var WithdrawalFees = map[string]float64{ + symbol.ZRX: 5, + symbol.ARDR: 2, + symbol.REP: 0.1, + symbol.BTC: 0.0005, + symbol.BCH: 0.0001, + symbol.XBC: 0.0001, + symbol.BTCD: 0.01, + symbol.BTM: 0.01, + symbol.BTS: 5, + symbol.BURST: 1, + symbol.BCN: 1, + symbol.CVC: 1, + symbol.CLAM: 0.001, + symbol.XCP: 1, + symbol.DASH: 0.01, + symbol.DCR: 0.1, + symbol.DGB: 0.1, + symbol.DOGE: 5, + symbol.EMC2: 0.01, + symbol.EOS: 0, + symbol.ETH: 0.01, + symbol.ETC: 0.01, + symbol.EXP: 0.01, + symbol.FCT: 0.01, + symbol.GAME: 0.01, + symbol.GAS: 0, + symbol.GNO: 0.015, + symbol.GNT: 1, + symbol.GRC: 0.01, + symbol.HUC: 0.01, + symbol.LBC: 0.05, + symbol.LSK: 0.1, + symbol.LTC: 0.001, + symbol.MAID: 10, + symbol.XMR: 0.015, + symbol.NMC: 0.01, + symbol.NAV: 0.01, + symbol.XEM: 15, + symbol.NEOS: 0.0001, + symbol.NXT: 1, + symbol.OMG: 0.3, + symbol.OMNI: 0.1, + symbol.PASC: 0.01, + symbol.PPC: 0.01, + symbol.POT: 0.01, + symbol.XPM: 0.01, + symbol.XRP: 0.15, + symbol.SC: 10, + symbol.STEEM: 0.01, + symbol.SBD: 0.01, + symbol.XLM: 0.00001, + symbol.STORJ: 1, + symbol.STRAT: 0.01, + symbol.AMP: 5, + symbol.SYS: 0.01, + symbol.USDT: 10, + symbol.VRC: 0.01, + symbol.VTC: 0.001, + symbol.VIA: 0.01, + symbol.ZEC: 0.001, +} diff --git a/exchanges/poloniex/poloniex_wrapper.go b/exchanges/poloniex/poloniex_wrapper.go index da204c12..beb3700d 100644 --- a/exchanges/poloniex/poloniex_wrapper.go +++ b/exchanges/poloniex/poloniex_wrapper.go @@ -148,14 +148,14 @@ func (p *Poloniex) GetExchangeFundTransferHistory() ([]exchange.FundHistory, err } // GetExchangeHistory returns historic trade data since exchange opening. -func (p *Poloniex) GetExchangeHistory(cP pair.CurrencyPair, assetType string) ([]exchange.TradeHistory, error) { +func (p *Poloniex) GetExchangeHistory(currencyPair pair.CurrencyPair, assetType string) ([]exchange.TradeHistory, error) { var resp []exchange.TradeHistory return resp, errors.New("trade history not yet implemented") } // SubmitExchangeOrder submits a new order -func (p *Poloniex) SubmitExchangeOrder(cP pair.CurrencyPair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (int64, error) { +func (p *Poloniex) SubmitExchangeOrder(currencyPair pair.CurrencyPair, side exchange.OrderSide, orderType exchange.OrderType, amount, price float64, clientID string) (int64, error) { return 0, errors.New("not yet implemented") } @@ -208,3 +208,8 @@ func (p *Poloniex) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Cu func (p *Poloniex) GetWebsocket() (*exchange.Websocket, error) { return p.Websocket, nil } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (p *Poloniex) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return p.GetFee(feeBuilder) +} diff --git a/exchanges/wex/wex.go b/exchanges/wex/wex.go index 00e004d4..8af95f9b 100644 --- a/exchanges/wex/wex.go +++ b/exchanges/wex/wex.go @@ -11,6 +11,7 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" exchange "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -127,11 +128,6 @@ func (w *WEX) GetTradablePairs() ([]string, error) { return currencies, nil } -// GetFee returns the exchange fee -func (w *WEX) GetFee() float64 { - return w.Fee -} - // GetInfo returns the WEX info func (w *WEX) GetInfo() (Info, error) { resp := Info{} @@ -399,3 +395,60 @@ func (w *WEX) SendAuthenticatedHTTPRequest(method string, values url.Values, res true, w.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (w *WEX) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + info, err := w.GetInfo() + if err != nil { + return 0, err + } + currency := feeBuilder.FirstCurrency + feeBuilder.Delimiter + feeBuilder.SecondCurrency + fee = calculateTradingFee(info, currency, feeBuilder.PurchasePrice, feeBuilder.Amount) + case exchange.CryptocurrencyWithdrawalFee: + fee = getWithdrawalFee(feeBuilder.FirstCurrency) + case exchange.InternationalBankDepositFee: + fee = getInternationalBankDepositFee(feeBuilder.CurrencyItem, feeBuilder.Amount, feeBuilder.BankTransactionType) + } + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(info Info, currency string, purchasePrice, amount float64) (fee float64) { + fee = info.Pairs[common.StringToLower(currency)].Fee + return (fee / 100) * amount * purchasePrice +} + +func getWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} + +func getInternationalBankDepositFee(currency string, amount float64, bankTransactionType exchange.InternationalBankTransactionType) float64 { + var fee float64 + + switch bankTransactionType { + case exchange.WireTransfer: + fallthrough + case exchange.WesternUnion: + switch currency { + case symbol.USD: + fee = 0.065 * amount + } + case exchange.MoneyGram: + switch currency { + case symbol.USD: + fee = 0.065 * amount + } + case exchange.Contact: + switch currency { + case symbol.USD: + fee = 0.065 * amount + } + } + return fee +} diff --git a/exchanges/wex/wex_test.go b/exchanges/wex/wex_test.go index 05789a2d..80027e82 100644 --- a/exchanges/wex/wex_test.go +++ b/exchanges/wex/wex_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var w WEX @@ -40,13 +42,6 @@ func TestGetTradablePairs(t *testing.T) { } } -func TestGetFee(t *testing.T) { - t.Parallel() - if w.GetFee() != 0.2 { - t.Error("Test Failed - GetFee() error") - } -} - func TestGetInfo(t *testing.T) { t.Parallel() _, err := w.GetInfo() @@ -166,3 +161,96 @@ func TestRedeemCoupon(t *testing.T) { t.Error("Test Failed - RedeemCoupon() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "_", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.LTC, + SecondCurrency: symbol.BTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + w.SetDefaults() + TestSetup(t) + var feeBuilder = setFeeBuilder() + + // CryptocurrencyTradeFee Basic + if resp, err := w.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := w.GetFee(feeBuilder); resp != float64(2000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2000), resp) + t.Error(err) + } + + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := w.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := w.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := w.GetFee(feeBuilder); resp != float64(0.001) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.001), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := w.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := w.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := w.GetFee(feeBuilder); resp != float64(0.065) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.065), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := w.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/wex/wex_types.go b/exchanges/wex/wex_types.go index ee2713b6..9bfc40eb 100644 --- a/exchanges/wex/wex_types.go +++ b/exchanges/wex/wex_types.go @@ -1,5 +1,7 @@ package wex +import "github.com/thrasher-/gocryptotrader/currency/symbol" + // Response is a generic struct used for exchange API request result type Response struct { Return interface{} `json:"return"` @@ -152,3 +154,19 @@ type RedeemCoupon struct { TransID int64 `json:"transID"` Error string `json:"error"` } + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change, using highest value +var WithdrawalFees = map[string]float64{ + symbol.BTC: 0.0004, + symbol.LTC: 0.001, + symbol.NMC: 0.1, + symbol.NVC: 0.1, + symbol.PPC: 0.1, + symbol.DSH: 0.001, + symbol.ETH: 0.003, + symbol.BCH: 0.001, + symbol.ZEC: 0.001, + symbol.USDT: 10, + symbol.XMR: 0.01, +} diff --git a/exchanges/wex/wex_wrapper.go b/exchanges/wex/wex_wrapper.go index f4de5188..5501a576 100644 --- a/exchanges/wex/wex_wrapper.go +++ b/exchanges/wex/wex_wrapper.go @@ -211,3 +211,8 @@ func (w *WEX) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Currenc func (w *WEX) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (w *WEX) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return w.GetFee(feeBuilder) +} diff --git a/exchanges/yobit/yobit.go b/exchanges/yobit/yobit.go index a490bdf5..d84b8073 100644 --- a/exchanges/yobit/yobit.go +++ b/exchanges/yobit/yobit.go @@ -11,6 +11,7 @@ import ( "github.com/thrasher-/gocryptotrader/common" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" "github.com/thrasher-/gocryptotrader/exchanges" "github.com/thrasher-/gocryptotrader/exchanges/request" "github.com/thrasher-/gocryptotrader/exchanges/ticker" @@ -112,11 +113,6 @@ func (y *Yobit) Setup(exch config.ExchangeConfig) { } } -// GetFee returns the exchange fee -func (y *Yobit) GetFee() float64 { - return y.Fee -} - // GetInfo returns the Yobit info func (y *Yobit) GetInfo() (Info, error) { resp := Info{} @@ -358,3 +354,110 @@ func (y *Yobit) SendAuthenticatedHTTPRequest(path string, params url.Values, res return y.SendPayload("POST", apiPrivateURL, headers, strings.NewReader(encoded), result, true, y.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (y *Yobit) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount) + case exchange.CryptocurrencyWithdrawalFee: + fee = getWithdrawalFee(feeBuilder.FirstCurrency) + case exchange.InternationalBankDepositFee: + fee = getInternationalBankDepositFee(feeBuilder.CurrencyItem, feeBuilder.Amount, feeBuilder.BankTransactionType) + case exchange.InternationalBankWithdrawalFee: + fee = getInternationalBankWithdrawalFee(feeBuilder.CurrencyItem, feeBuilder.Amount, feeBuilder.BankTransactionType) + } + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(purchasePrice, amount float64) (fee float64) { + fee = 0.002 + return fee * amount * purchasePrice +} + +func getWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} + +func getInternationalBankWithdrawalFee(currency string, amount float64, bankTransactionType exchange.InternationalBankTransactionType) float64 { + var fee float64 + + switch bankTransactionType { + case exchange.PerfectMoney: + switch currency { + case symbol.USD: + fee = 0.02 * amount + } + case exchange.Payeer: + switch currency { + case symbol.USD: + fee = 0.03 * amount + case symbol.RUR: + fee = 0.006 * amount + } + case exchange.AdvCash: + switch currency { + case symbol.USD: + fee = 0.04 * amount + case symbol.RUR: + fee = 0.03 * amount + } + case exchange.Qiwi: + switch currency { + case symbol.RUR: + fee = 0.04 * amount + } + case exchange.Capitalist: + switch currency { + case symbol.USD: + fee = 0.06 * amount + } + } + + return fee +} + +// No real fees for yobit deposits, but want to be explicit on what each payment type supports +func getInternationalBankDepositFee(currency string, amount float64, bankTransactionType exchange.InternationalBankTransactionType) float64 { + var fee float64 + switch bankTransactionType { + case exchange.PerfectMoney: + switch currency { + case symbol.USD: + fee = 0 + } + case exchange.Payeer: + switch currency { + case symbol.USD: + fee = 0 + case symbol.RUR: + fee = 0 + } + case exchange.AdvCash: + switch currency { + case symbol.USD: + fee = 0 + case symbol.RUR: + fee = 0 + } + case exchange.Qiwi: + switch currency { + case symbol.RUR: + fee = 0 + } + case exchange.Capitalist: + switch currency { + case symbol.USD: + fee = 0 + case symbol.RUR: + fee = 0 + } + } + + return fee +} diff --git a/exchanges/yobit/yobit_test.go b/exchanges/yobit/yobit_test.go index de94c8ff..31c74f47 100644 --- a/exchanges/yobit/yobit_test.go +++ b/exchanges/yobit/yobit_test.go @@ -4,6 +4,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) var y Yobit @@ -32,13 +34,6 @@ func TestSetup(t *testing.T) { y.Setup(conf) } -func TestGetFee(t *testing.T) { - t.Parallel() - if y.GetFee() != 0.2 { - t.Error("Test Failed - GetFee() error") - } -} - func TestGetInfo(t *testing.T) { t.Parallel() _, err := y.GetInfo() @@ -150,3 +145,155 @@ func TestRedeemYobicode(t *testing.T) { t.Error("Test Failed - RedeemYobicode() error", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "-", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.LTC, + SecondCurrency: symbol.BTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + y.SetDefaults() + TestSetup(t) + var feeBuilder = setFeeBuilder() + + // CryptocurrencyTradeFee Basic + if resp, err := y.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0015), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := y.GetFee(feeBuilder); resp != float64(2000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := y.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := y.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := y.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := y.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := y.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := y.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := y.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee QIWI + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + feeBuilder.BankTransactionType = exchange.Qiwi + if resp, err := y.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Wire + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + feeBuilder.BankTransactionType = exchange.WireTransfer + if resp, err := y.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Payeer + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + feeBuilder.BankTransactionType = exchange.Payeer + if resp, err := y.GetFee(feeBuilder); resp != float64(0.03) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.03), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Capitalist + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.RUR + feeBuilder.BankTransactionType = exchange.Capitalist + if resp, err := y.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee AdvCash + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + feeBuilder.BankTransactionType = exchange.AdvCash + if resp, err := y.GetFee(feeBuilder); resp != float64(0.04) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.04), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee PerfectMoney + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.RUR + feeBuilder.BankTransactionType = exchange.PerfectMoney + if resp, err := y.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/yobit/yobit_types.go b/exchanges/yobit/yobit_types.go index 1990039f..10a37353 100644 --- a/exchanges/yobit/yobit_types.go +++ b/exchanges/yobit/yobit_types.go @@ -1,5 +1,7 @@ package yobit +import "github.com/thrasher-/gocryptotrader/currency/symbol" + // Response is a generic struct used for exchange API request result type Response struct { Return interface{} `json:"return"` @@ -144,3 +146,1147 @@ type RedeemCoupon struct { Funds map[string]float64 `json:"funds"` Error string `json:"error"` } + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change, using highest value +var WithdrawalFees = map[string]float64{ + symbol.ZERO07: 0.002, + symbol.BIT16: 0.002, + symbol.TWO015: 0.002, + symbol.TWO56: 0.0002, + symbol.TWOBACCO: 0.002, + symbol.TWOGIVE: 0.01, + symbol.THIRTY2BIT: 0.002, + symbol.THREE65: 0.01, + symbol.FOUR04: 0.01, + symbol.SEVEN00: 0.01, + symbol.EIGHTBIT: 0.002, + symbol.ACLR: 0.002, + symbol.ACES: 0.01, + symbol.ACPR: 0.01, + symbol.ACID: 0.01, + symbol.ACOIN: 0.01, + symbol.ACRN: 0.01, + symbol.ADAM: 0.01, + symbol.ADT: 0.05, + symbol.AIB: 0.01, + symbol.ADZ: 0.002, + symbol.AECC: 0.002, + symbol.AM: 0.002, + symbol.AE: 10, + symbol.DLT: 0.05, + symbol.AGRI: 0.01, + symbol.AGT: 0.01, + symbol.AST: 0.002, + symbol.AIR: 0.01, + symbol.ALEX: 0.01, + symbol.AUM: 0.002, + symbol.ALIEN: 0.01, + symbol.ALIS: 0.05, + symbol.ALL: 0.01, + symbol.ASAFE: 0.01, + symbol.AMBER: 0.002, + symbol.AMS: 0.002, + symbol.ANAL: 0.002, + symbol.ACP: 0.002, + symbol.ANI: 0.01, + symbol.ANTI: 0.002, + symbol.ALTC: 0.01, + symbol.APT: 0.01, + symbol.ARCO: 0.002, + symbol.ALC: 0.01, + symbol.ANT: 0.01, + symbol.ARB: 0.002, + symbol.ARCT: 10, + symbol.ARCX: 0.01, + symbol.ARGUS: 0.01, + symbol.ARH: 0.01, + symbol.ARM: 0.01, + symbol.ARNA: 10, + symbol.ARPA: 0.002, + symbol.ARTA: 0.01, + symbol.ABY: 0.01, + symbol.ARTC: 0.01, + symbol.AL: 0.01, + symbol.ASN: 0.002, + symbol.ADCN: 0.01, + symbol.ATB: 0.01, + symbol.ATL: 0.1, + symbol.ATM: 0.002, + symbol.ATMCHA: 0.05, + symbol.ATOM: 0.01, + symbol.ADC: 0.002, + symbol.REP: 0.002, + symbol.ARE: 0.002, + symbol.AUR: 0.01, + symbol.AV: 0.002, + symbol.AXIOM: 0.002, + symbol.B2B: 10, + symbol.B2: 0.01, + symbol.B3: 0.1, + symbol.BAB: 0.01, + symbol.BAN: 0.002, + symbol.BamitCoin: 0.002, + symbol.NANAS: 0.002, + symbol.BNT: 0.05, + symbol.BBCC: 0.002, + symbol.BAT: 0.05, + symbol.BTA: 0.002, + symbol.BSTK: 0.002, + symbol.BATL: 0.01, + symbol.BBH: 0.01, + symbol.BITB: 0.002, + symbol.BRDD: 0.002, + symbol.XBTS: 0.01, + symbol.BVC: 0.01, + symbol.CHATX: 10, + symbol.BEEP: 0.01, + symbol.BEEZ: 0.002, + symbol.BENJI: 0.01, + symbol.BERN: 0.002, + symbol.PROFIT: 0.01, + symbol.BEST: 0.01, + symbol.BGF: 0.01, + symbol.BIGUP: 0.002, + symbol.BLRY: 0.01, + symbol.BILL: 0.01, + symbol.BNB: 0.002, + symbol.BIOB: 0.01, + symbol.BIO: 0.1, + symbol.BIOS: 0.002, + symbol.BPTN: 10, + symbol.BTCA: 10, + symbol.BA: 0.002, + symbol.BAC: 0.002, + symbol.BBT: 10, + symbol.BOSS: 0.01, + symbol.BRONZ: 0.002, + symbol.CAT: 0.01, + symbol.BTD: 0.01, + symbol.BTC: 0.0012, + symbol.XBTC21: 0.01, + symbol.BCA: 0.01, + symbol.BCH: 0.01, + symbol.BCP: 0.01, + symbol.BCD: 0.01, + symbol.BTDOLL: 0.01, + symbol.GOD: 0.01, + symbol.BTG: 0.01, + symbol.LIZA: 0.01, + symbol.BTCRED: 10, + symbol.BTCS: 0.01, + symbol.BTU: 0.01, + symbol.BUM: 0.01, + symbol.LITE: 0.01, + symbol.BCM: 0.01, + symbol.BCS: 0.01, + symbol.BTCU: 0.002, + symbol.BM: 10, + symbol.BTCRY: 0.002, + symbol.BTCR: 0.002, + symbol.HIRE: 0.002, + symbol.STU: 10, + symbol.BITOK: 0.0001, + symbol.BITON: 0.002, + symbol.BPC: 0.01, + symbol.BPOK: 0.01, + symbol.BTP: 0.002, + symbol.RNTB: 10, + symbol.BSH: 0.002, + symbol.BTS: 5, + symbol.XBS: 0.002, + symbol.BITS: 0.01, + symbol.BST: 0.002, + symbol.BXT: 0.01, + symbol.VEG: 0.002, + symbol.VOLT: 0.01, + symbol.BTV: 0.01, + symbol.BITZ: 0.002, + symbol.BTZ: 0.002, + symbol.BHC: 0.01, + symbol.BDC: 0.002, + symbol.JACK: 0.01, + symbol.BS: 0.01, + symbol.BSTAR: 0.01, + symbol.BLAZR: 0.01, + symbol.BOD: 0.002, + symbol.BLUE: 10, + symbol.BLU: 0.002, + symbol.BLUS: 0.002, + symbol.BMT: 10, + symbol.BOT: 0.002, + symbol.BOLI: 0.002, + symbol.BOMB: 0.01, + symbol.BON: 0.01, + symbol.BOOM: 0.002, + symbol.BOSON: 0.01, + symbol.BSC: 0.002, + symbol.BRH: 10, + symbol.BRAIN: 0.01, + symbol.BRE: 0.002, + symbol.BTCM: 0.1, + symbol.BTCO: 0.01, + symbol.TALK: 0.01, + symbol.BUB: 0.002, + symbol.BUY: 0.01, + symbol.BUZZ: 0.002, + symbol.BTH: 0.1, + symbol.C0C0: 0.002, + symbol.CAB: 0.01, + symbol.CF: 0.002, + symbol.CLO: 10, + symbol.CAM: 0.2, + symbol.CD: 0.002, + symbol.CANN: 0.2, + symbol.CNNC: 0.01, + symbol.CPC: 0.002, + symbol.CST: 0.01, + symbol.CAPT: 0.002, + symbol.CARBON: 0.01, + symbol.CME: 0.002, + symbol.CTK: 0.002, + symbol.CBD: 0.01, + symbol.CCC: 0.01, + symbol.CNT: 0.01, + symbol.XCE: 0.002, + symbol.CAG: 1, + symbol.CHRG: 0.01, + symbol.CHAT: 0.01, + symbol.CHEMX: 0.01, + symbol.CHESS: 0.01, + symbol.CKS: 0.01, + symbol.CHILL: 0.01, + symbol.CHIP: 0.002, + symbol.CHOOF: 0.01, + symbol.TIME: 0.05, + symbol.CRX: 0.01, + symbol.CIN: 0.01, + symbol.CLAM: 0.002, + symbol.POLL: 10, + symbol.CLICK: 0.002, + symbol.CLINT: 0.01, + symbol.CLOAK: 0.002, + symbol.CLUB: 0.002, + symbol.CLUD: 0.01, + symbol.COX: 0.01, + symbol.COXST: 0.01, + symbol.CFC: 0.002, + symbol.CTIC2: 0.01, + symbol.COIN: 0.01, + symbol.BTTF: 0.002, + symbol.C2: 0.01, + symbol.CAID: 0.002, + symbol.CL: 10, + symbol.CTIC: 0.01, + symbol.CXT: 0.01, + symbol.CHP: 10, + symbol.CV2: 0.002, + symbol.CMT: 0.01, + symbol.COC: 0.01, + symbol.COMP: 0.01, + symbol.CMS: 10, + symbol.CONX: 0.01, + symbol.CCX: 0.01, + symbol.CLR: 10, + symbol.CORAL: 0.01, + symbol.CORG: 0.01, + symbol.CSMIC: 0.01, + symbol.CMC: 0.01, + symbol.COV: 0.002, + symbol.COVX: 10, + symbol.CRAB: 0.01, + symbol.CRAFT: 0.01, + symbol.CRNK: 0.01, + symbol.CRAVE: 0.002, + symbol.CRM: 0.01, + symbol.XCRE: 0.01, + symbol.CREDIT: 0.002, + symbol.CREVA: 0.002, + symbol.CRIME: 0.002, + symbol.CROC: 0.01, + symbol.CRC: 10, + symbol.CRW: 0.002, + symbol.CRY: 0.002, + symbol.CBX: 0.002, + symbol.TKTX: 10, + symbol.CB: 0.02, + symbol.CIRC: 0.002, + symbol.CCB: 0.002, + symbol.CDO: 0.01, + symbol.CG: 0.01, + symbol.CJ: 0.01, + symbol.CJC: 0.01, + symbol.CYT: 0.002, + symbol.CNX: 0.01, + symbol.CRPS: 0.002, + symbol.PING: 0.05, + symbol.CS: 0.002, + symbol.CWXT: 0.01, + symbol.CCT: 0.05, + symbol.CTL: 0.01, + symbol.CURVES: 0.002, + symbol.CC: 0.002, + symbol.CYC: 0.002, + symbol.CYG: 0.002, + symbol.CYP: 0.002, + symbol.FUNK: 0.01, + symbol.CZECO: 0.01, + symbol.DALC: 0.1, + symbol.DLISK: 0.2, + symbol.MOOND: 0.002, + symbol.DB: 0.002, + symbol.DCC: 0.002, + symbol.DCYP: 0.002, + symbol.DETH: 0.002, + symbol.DKC: 0.01, + symbol.DISK: 0.01, + symbol.DRKT: 0.002, + symbol.DTT: 0.002, + symbol.DASH: 0.002, + symbol.DASHS: 0.01, + symbol.DBTC: 0.01, + symbol.DCT: 0.002, + symbol.DBET: 10, + symbol.DEC: 0.002, + symbol.DCR: 0.05, + symbol.DECR: 0.002, + symbol.DEA: 0.01, + symbol.DPAY: 0.01, + symbol.DCRE: 0.002, + symbol.DC: 0.002, + symbol.DES: 0.01, + symbol.DEM: 0.002, + symbol.DXC: 0.01, + symbol.DCK: 0.01, + symbol.DGB: 0.002, + symbol.CUBE: 0.002, + symbol.DGMS: 0.002, + symbol.DBG: 0.01, + symbol.DGCS: 0.002, + symbol.DBLK: 0.002, + symbol.DGD: 0.002, + symbol.DIME: 0.002, + symbol.DIRT: 0.002, + symbol.DVD: 10, + symbol.DMT: 10, + symbol.NOTE: 0.002, + symbol.DOGE: 100, + symbol.DGORE: 0.002, + symbol.DLC: 0.01, + symbol.DRT: 0.1, + symbol.DOTA: 0.01, + symbol.DOX: 0.002, + symbol.DRA: 0.002, + symbol.DFT: 0.002, + symbol.XDB: 0.002, + symbol.DRM: 0.002, + symbol.DRZ: 0.002, + symbol.DRACO: 0.002, + symbol.DBIC: 0.002, + symbol.DUB: 0.002, + symbol.GUM: 0.002, + symbol.DUR: 0.01, + symbol.DUST: 0.002, + symbol.DUX: 0.01, + symbol.DXO: 0.01, + symbol.ECN: 0.01, + symbol.EDR2: 0.002, + symbol.EA: 0.002, + symbol.EAGS: 0.002, + symbol.EMT: 10, + symbol.EBONUS: 0.001, + symbol.ECCHI: 0.01, + symbol.EKO: 0.01, + symbol.ECLI: 0.002, + symbol.ECOB: 3, + symbol.ECO: 0.01, + symbol.EDIT: 0.01, + symbol.EDRC: 0.01, + symbol.EDC: 0.01, + symbol.EGAME: 0.01, + symbol.EGG: 0.002, + symbol.EGO: 0.01, + symbol.ELC: 0.01, + symbol.ELCO: 0.01, + symbol.ECA: 0.01, + symbol.EPC: 0.002, + symbol.ELE: 0.005, + symbol.ONE337: 0.002, + symbol.EMB: 0.01, + symbol.EMC: 0.02, + symbol.EPY: 0.002, + symbol.EMPC: 0.01, + symbol.EMP: 0.002, + symbol.ENE: 0.01, + symbol.EET: 10, + symbol.XNG: 0.01, + symbol.EGMA: 0.002, + symbol.ENTER: 0.01, + symbol.ETRUST: 0.002, + symbol.EOS: 10, + symbol.EQL: 0.01, + symbol.EQM: 0.002, + symbol.EQT: 0.01, + symbol.ERR: 0.002, + symbol.ESC: 0.002, + symbol.ESP: 0.01, + symbol.ENT: 0.01, + symbol.ETCO: 0.2, + symbol.DOGETH: 0.002, + symbol.ETH: 0.005, + symbol.ECASH: 0.1, + symbol.ETC: 0.005, + symbol.ELITE: 0.05, + symbol.ETHS: 0.01, + symbol.ETL: 1, + symbol.ETZ: 10, + symbol.EUC: 0.01, + symbol.EURC: 0.002, + symbol.EUROPE: 0.01, + symbol.EVA: 0.01, + symbol.EGC: 0.002, + symbol.EOC: 0.002, + symbol.EVIL: 0.002, + symbol.EVO: 0.002, + symbol.EXB: 0.002, + symbol.EXIT: 0.01, + symbol.EXP: 0.01, + symbol.XT: 0.01, + symbol.F16: 0.01, + symbol.FADE: 0.002, + symbol.DROP: 0.002, + symbol.FAZZ: 0.01, + symbol.FX: 0.01, + symbol.FIDEL: 0.01, + symbol.FIDGT: 0.01, + symbol.FIND: 0.002, + symbol.FPC: 0.01, + symbol.FIRE: 0.002, + symbol.FFC: 0.002, + symbol.FRST: 0.01, + symbol.FIST: 0.002, + symbol.FIT: 0.05, + symbol.FLX: 0.01, + symbol.FLVR: 0.01, + symbol.FLY: 0.002, + symbol.FONZ: 0.002, + symbol.XFCX: 0.002, + symbol.FOREX: 0.01, + symbol.FRN: 0.002, + symbol.FRK: 0.002, + symbol.FRWC: 0.01, + symbol.FGZ: 0.01, + symbol.FRE: 0.002, + symbol.FRDC: 0.002, + symbol.FJC: 0.01, + symbol.FURY: 0.002, + symbol.FSN: 0.002, + symbol.FCASH: 0.002, + symbol.FTO: 0.01, + symbol.FUZZ: 0.002, + symbol.GAKH: 0.01, + symbol.GBT: 0.01, + symbol.GAME: 0.01, + symbol.GML: 0.2, + symbol.UNITS: 0.01, + symbol.FOUR20G: 0.01, + symbol.GSY: 0.002, + symbol.GENIUS: 0.002, + symbol.GEN: 0.002, + symbol.GEO: 0.002, + symbol.GER: 0.01, + symbol.GSR: 0.01, + symbol.SPKTR: 0.002, + symbol.GIFT: 0.002, + symbol.WTT: 10, + symbol.GHS: 0.01, + symbol.GIG: 0.002, + symbol.GOT: 0.01, + symbol.XGTC: 0.01, + symbol.GIZ: 0.002, + symbol.GLO: 0.01, + symbol.GCR: 0.002, + symbol.BSTY: 0.002, + symbol.GLC: 0.01, + symbol.GSX: 0.02, + symbol.GNO: 0.05, + symbol.GOAT: 0.002, + symbol.GO: 0.01, + symbol.GB: 0.01, + symbol.GFL: 0.01, + symbol.MNTP: 10, + symbol.GP: 0.002, + symbol.GNT: 0.05, + symbol.GLUCK: 0.002, + symbol.GOON: 0.01, + symbol.GTFO: 0.002, + symbol.GOTX: 0.01, + symbol.GPU: 0.01, + symbol.GRF: 0.002, + symbol.GRAM: 0.002, + symbol.GRAV: 0.002, + symbol.GBIT: 0.002, + symbol.GREED: 0.002, + symbol.GE: 0.002, + symbol.GREENF: 0.01, + symbol.GRE: 0.01, + symbol.GREXIT: 0.002, + symbol.GMCX: 0.002, + symbol.GROW: 0.01, + symbol.GSM: 0.002, + symbol.GT: 0.01, + symbol.NLG: 0.01, + symbol.HKN: 10, + symbol.HAC: 0.05, + symbol.HALLO: 0.01, + symbol.HAMS: 0.01, + symbol.HCC: 0.01, + symbol.HPC: 0.01, + symbol.HMC: 0.01, + symbol.HAWK: 0.01, + symbol.HAZE: 0.002, + symbol.HZT: 0.002, + symbol.HDG: 0.1, + symbol.HEDG: 0.002, + symbol.HEEL: 0.002, + symbol.HMP: 0.01, + symbol.PLAY: 0.01, + symbol.HXX: 0.002, + symbol.XHI: 0.01, + symbol.HVCO: 0.01, + symbol.HTC: 0.01, + symbol.MINH: 0.01, + symbol.HODL: 0.01, + symbol.HON: 0.01, + symbol.HOPE: 0.01, + symbol.HQX: 10, + symbol.HSP: 0.002, + symbol.HTML5: 0.002, + symbol.HMQ: 0.01, + symbol.HYPERX: 0.01, + symbol.HPS: 10, + symbol.IOC: 0.002, + symbol.IBANK: 0.01, + symbol.IBITS: 0.002, + symbol.ICASH: 0.002, + symbol.ICOB: 0.01, + symbol.ICN: 0.002, + symbol.ICON: 0.01, + symbol.IETH: 0.1, + symbol.ILM: 0.002, + symbol.IMPS: 0.01, + symbol.NKA: 0.002, + symbol.INCP: 0.01, + symbol.IN: 0.01, + symbol.INC: 0.002, + symbol.IMS: 0.01, + symbol.IND: 0.01, + symbol.XIN: 0.01, + symbol.IFLT: 0.01, + symbol.INFX: 0.002, + symbol.INGT: 0.01, + symbol.INPAY: 0.01, + symbol.INSANE: 0.01, + symbol.INXT: 0.01, + symbol.IFT: 0.05, + symbol.INV: 0.01, + symbol.IVZ: 0.002, + symbol.ILT: 0.002, + symbol.IONX: 0.01, + symbol.ISL: 0.002, + symbol.ITI: 0.01, + symbol.ING: 10, + symbol.IEC: 0.002, + symbol.IW: 0.01, + symbol.IXC: 0.01, + symbol.IXT: 0.05, + symbol.JPC: 0.002, + symbol.JANE: 0.01, + symbol.JWL: 0.01, + symbol.JNT: 0.01, + symbol.JIF: 0.002, + symbol.JOBS: 0.01, + symbol.JOCKER: 0.01, + symbol.JW: 0.01, + symbol.JOK: 0.01, + symbol.XJO: 0.002, + symbol.KGB: 0.01, + symbol.KARMC: 0.01, + symbol.KARMA: 0.002, + symbol.KASHH: 0.01, + symbol.KAT: 0.002, + symbol.KC: 0.002, + symbol.KICK: 0.05, + symbol.KIDS: 0.01, + symbol.KIN: 10, + symbol.KNC: 0.01, + symbol.KISS: 0.01, + symbol.KOBO: 0.002, + symbol.TP1: 0.002, + symbol.KRAK: 0.002, + symbol.KGC: 0.002, + symbol.KTK: 0.002, + symbol.KR: 0.005, + symbol.KUBO: 0.01, + symbol.KURT: 0.01, + symbol.KUSH: 0.01, + symbol.LANA: 0.01, + symbol.LTH: 0.01, + symbol.LAZ: 0.2, + symbol.LEA: 0.002, + symbol.LEAF: 0.002, + symbol.LENIN: 0.01, + symbol.LEPEN: 0.01, + symbol.LIR: 0.01, + symbol.LVG: 0.002, + symbol.LGBTQ: 0.002, + symbol.LHC: 10, + symbol.EXT: 0.002, + symbol.LBTC: 0.1, + symbol.LSD: 0.01, + symbol.LIMX: 0.002, + symbol.LTD: 0.0000002, + symbol.LINDA: 0.01, + symbol.LKC: 0.002, + symbol.LSK: 0.2, + symbol.LBTCX: 0.01, + symbol.LTC: 0.002, + symbol.LCC: 1, + symbol.LTCU: 0.01, + symbol.LTCR: 0.002, + symbol.LDOGE: 0.002, + symbol.LTS: 0.002, + symbol.LIV: 0.01, + symbol.LIZI: 0.01, + symbol.LOC: 0.01, + symbol.LOCX: 10, + symbol.LOOK: 0.01, + symbol.LRC: 0.05, + symbol.LOOT: 0.01, + symbol.XLTCG: 0.002, + symbol.BASH: 0.01, + symbol.LUCKY: 0.002, + symbol.L7S: 0.002, + symbol.LDM: 0.05, + symbol.LUMI: 0.01, + symbol.LUNA: 0.01, + symbol.LUN: 0.002, + symbol.LC: 0.01, + symbol.LUX: 0.002, + symbol.MCRN: 0.01, + symbol.XMG: 0.01, + symbol.MMXIV: 0.02, + symbol.MAT: 0.01, + symbol.MAO: 0.01, + symbol.MAPC: 0.002, + symbol.MRB: 0.002, + symbol.MXT: 0.01, + symbol.MARV: 0.01, + symbol.MARX: 0.01, + symbol.MCAR: 0.002, + symbol.MM: 0.002, + symbol.GUP: 0.05, + symbol.MVC: 0.05, + symbol.MAVRO: 0.01, + symbol.MAX: 0.01, + symbol.MAZE: 0.002, + symbol.MBIT: 0.01, + symbol.MCOIN: 0.01, + symbol.MPRO: 0.01, + symbol.XMS: 0.002, + symbol.MLITE: 0.01, + symbol.MLNC: 0.01, + symbol.MENTAL: 0.01, + symbol.MERGEC: 0.01, + symbol.MTLMC3: 0.002, + symbol.METAL: 0.002, + symbol.AMM: 0.01, + symbol.MDT: 0.002, + symbol.MUU: 0.01, + symbol.MILO: 0.01, + symbol.MND: 0.002, + symbol.XMINE: 0.002, + symbol.MNM: 0.01, + symbol.XNM: 0.01, + symbol.MIRO: 10, + symbol.MIS: 0.002, + symbol.MMXVI: 0.01, + symbol.MGO: 0.01, + symbol.MOIN: 0.002, + symbol.MOJO: 0.01, + symbol.TAB: 0.002, + symbol.MCO: 0.005, + symbol.MONETA: 0.002, + symbol.MUE: 0.002, + symbol.MONEY: 0.01, + symbol.MRP: 0.002, + symbol.MOTO: 0.002, + symbol.MULTI: 0.01, + symbol.MST: 0.01, + symbol.MVR: 0.01, + symbol.MYSTIC: 0.002, + symbol.WISH: 10, + symbol.NKT: 0.002, + symbol.NMC: 0.002, + symbol.NAT: 0.002, + symbol.ENAU: 10, + symbol.NAV: 0.002, + symbol.NEBU: 0.002, + symbol.NEF: 0.01, + symbol.XEM: 20, + symbol.NBIT: 0.01, + symbol.NETKO: 0.01, + symbol.NTM: 0.01, + symbol.NETC: 0.002, + symbol.NEU: 10, + symbol.NRC: 1, + symbol.NTK: 10, + symbol.NTRN: 0.002, + symbol.NEVA: 0.01, + symbol.NIC: 0.01, + symbol.NKC: 0.002, + symbol.NYC: 0.01, + symbol.NZC: 0.01, + symbol.NICE: 0.002, + symbol.NET: 0.01, + symbol.NDOGE: 0.002, + symbol.XTR: 0.01, + symbol.N2O: 0.002, + symbol.NIXON: 0.01, + symbol.NOC: 0.002, + symbol.NODC: 0.01, + symbol.NODES: 0.002, + symbol.NODX: 0.002, + symbol.NLC: 0.01, + symbol.NLC2: 0.01, + symbol.NOO: 0.002, + symbol.NVC: 0.002, + symbol.NPC: 0.002, + symbol.NUBIS: 0.002, + symbol.NUKE: 0.002, + symbol.N7: 0.01, + symbol.NUM: 0.01, + symbol.NMR: 0.05, + symbol.NXE: 0.002, + symbol.OBS: 0.002, + symbol.OCEAN: 0.01, + symbol.OCOW: 0.01, + symbol.EIGHT88: 0.02, + symbol.OCC: 0.02, + symbol.OK: 0.002, + symbol.ODNT: 0.002, + symbol.FLAV: 0.002, + symbol.OLIT: 0.01, + symbol.OLYMP: 0.01, + symbol.OMA: 0.002, + symbol.OMC: 0.01, + symbol.OMG: 0.01, + symbol.ONEK: 0.05, + symbol.ONX: 0.01, + symbol.XPO: 0.01, + symbol.OPAL: 0.2, + symbol.OTN: 0.1, + symbol.OP: 0.01, + symbol.OPES: 0.002, + symbol.OPTION: 0.002, + symbol.ORLY: 0.01, + symbol.OS76: 0.002, + symbol.OZC: 0.002, + symbol.P7C: 0.002, + symbol.PAC: 1, + symbol.PAK: 0.002, + symbol.PAL: 0.01, + symbol.PND: 0.002, + symbol.PINKX: 0.01, + symbol.POPPY: 0.01, + symbol.DUO: 0.002, + symbol.PARA: 0.002, + symbol.PKB: 0.002, + symbol.GENE: 0.002, + symbol.PARTY: 0.01, + symbol.PYN: 10, + symbol.XPY: 0.002, + symbol.CON: 0.002, + symbol.PAYP: 0.01, + symbol.PPC: 0.2, + symbol.GUESS: 10, + symbol.PEN: 0.002, + symbol.PTA: 0.002, + symbol.PEO: 0.002, + symbol.PSB: 0.01, + symbol.XPD: 0.01, + symbol.PXL: 0.002, + symbol.PHR: 0.002, + symbol.PIE: 0.01, + symbol.PIO: 0.01, + symbol.PIPR: 0.01, + symbol.SKULL: 0.01, + symbol.PIVX: 0.002, + symbol.PLANET: 0.002, + symbol.PNC: 0.002, + symbol.XPTX: 0.01, + symbol.PLNC: 0.002, + symbol.PLU: 0.01, + symbol.XPS: 0.01, + symbol.POKE: 0.01, + symbol.PLBT: 0.01, + symbol.POLY: 0.002, + symbol.POM: 0.001, + symbol.PONZ2: 0.01, + symbol.PONZI: 0.01, + symbol.XSP: 0.002, + symbol.PPT: 10, + symbol.XPC: 0.002, + symbol.PEX: 0.002, + symbol.TRON: 0.002, + symbol.POST: 0.01, + symbol.POSW: 0.01, + symbol.PWR: 0.01, + symbol.POWER: 0.002, + symbol.PRE: 0.002, + symbol.PRS: 10, + symbol.PXI: 0.002, + symbol.PEXT: 10, + symbol.PRIMU: 0.01, + symbol.PRX: 0.01, + symbol.PRM: 0.01, + symbol.PRIX: 10, + symbol.XPRO: 0.002, + symbol.PCM: 0.01, + symbol.PROC: 0.01, + symbol.NANOX: 0.01, + symbol.VRP: 0.01, + symbol.PTY: 0.002, + symbol.PSI: 0.002, + symbol.PSY: 0.002, + symbol.PULSE: 0.01, + symbol.PUPA: 0.01, + symbol.PURE: 0.002, + symbol.VIDZ: 0.01, + symbol.PUTIN: 0.01, + symbol.PX: 0.1, + symbol.QTM: 0.01, + symbol.QTZ: 0.002, + symbol.QBC: 0.01, + symbol.XQN: 0.02, + symbol.RBBT: 0.01, + symbol.RAC: 10, + symbol.RADI: 0.01, + symbol.RAD: 0.002, + symbol.RAI: 0.01, + symbol.XRA: 0.002, + symbol.RATIO: 0.002, + symbol.RCN: 0.01, + symbol.REA: 10, + symbol.RCX: 0.01, + symbol.RDD: 0.002, + symbol.REE: 0.01, + symbol.REC: 0.01, + symbol.REQ: 10, + symbol.RMS: 0.002, + symbol.RBIT: 0.01, + symbol.RNC: 0.002, + symbol.R: 1, + symbol.REV: 0.01, + symbol.RH: 0.01, + symbol.XRL: 1, + symbol.RICE: 0.002, + symbol.RICHX: 0.002, + symbol.RID: 0.01, + symbol.RIDE: 0.01, + symbol.RBT: 0.002, + symbol.RING: 0.01, + symbol.RIO: 0.01, + symbol.RISE: 0.2, + symbol.ROCKET: 0.01, + symbol.RPC: 0.01, + symbol.ROS: 0.002, + symbol.ROYAL: 0.01, + symbol.RSGP: 0.01, + symbol.RBIES: 0.002, + symbol.RUBIT: 0.002, + symbol.RBY: 0.01, + symbol.RUC: 0.01, + symbol.RUPX: 0.01, + symbol.RUP: 0.01, + symbol.RUST: 0.01, + symbol.SFE: 0.01, + symbol.SLS: 0.002, + symbol.SMSR: 0.002, + symbol.RONIN: 0.002, + symbol.SAN: 0.05, + symbol.STV: 0.002, + symbol.HIFUN: 0.002, + symbol.MAD: 0.002, + symbol.SANDG: 0.002, + symbol.STO: 0.01, + symbol.SCAN: 0.01, + symbol.SCITW: 0.002, + symbol.SCRPT: 0.01, + symbol.SCRT: 0.002, + symbol.SED: 0.002, + symbol.SEEDS: 0.002, + symbol.B2X: 0.1, + symbol.SEL: 0.01, + symbol.SLFI: 0.002, + symbol.SSC: 0.002, + symbol.SMBR: 0.2, + symbol.SEN: 0.002, + symbol.SENT: 10, + symbol.SRNT: 10, + symbol.SEV: 0.01, + symbol.SP: 0.01, + symbol.SXC: 0.002, + symbol.GELD: 0.05, + symbol.SHDW: 0.01, + symbol.SDC: 0.02, + symbol.SAK: 0.002, + symbol.SHRP: 0.01, + symbol.SHELL: 0.002, + symbol.SH: 0.01, + symbol.SHORTY: 0.01, + symbol.SHREK: 0.01, + symbol.SHRM: 0.002, + symbol.SIB: 0.002, + symbol.SIGT: 0.01, + symbol.SLCO: 0.01, + symbol.SIGU: 0.002, + symbol.SIX: 0.002, + symbol.SJW: 0.002, + symbol.SKB: 0.002, + symbol.SW: 0.01, + symbol.SLEEP: 0.01, + symbol.SLING: 0.01, + symbol.SMART: 0.01, + symbol.SMC: 0.002, + symbol.SMT: 10, + symbol.SMF: 0.01, + symbol.SOCC: 0.01, + symbol.SCL: 0.05, + symbol.SDAO: 10, + symbol.SOLAR: 0.01, + symbol.SOLO: 0.002, + symbol.SCT: 0.01, + symbol.SONG: 0.01, + symbol.SNM: 0.05, + symbol.ALTCOM: 0.01, + symbol.SPHTX: 10, + symbol.SOUL: 0.01, + symbol.SPC: 0.002, + symbol.SPACE: 0.002, + symbol.SBT: 0.01, + symbol.SPEC: 0.002, + symbol.SPX: 0.002, + symbol.SCS: 0.01, + symbol.SPORT: 0.01, + symbol.SPT: 0.002, + symbol.SPR: 0.002, + symbol.SPEX: 0.002, + symbol.SQL: 0.002, + symbol.SBIT: 0.002, + symbol.STHR: 0.002, + symbol.STALIN: 0.01, + symbol.STAR: 0.01, + symbol.STA: 0.01, + symbol.START: 0.02, + symbol.STP: 0.002, + symbol.SNT: 10, + symbol.PNK: 0.002, + symbol.STEPS: 0.002, + symbol.STK: 0.002, + symbol.STONK: 0.01, + symbol.STORJ: 0.05, + symbol.STORM: 10, + symbol.STS: 0.002, + symbol.STRP: 0.002, + symbol.STY: 10, + symbol.SUB: 0.01, + symbol.XMT: 0.002, + symbol.SNC: 1, + symbol.SSTC: 0.01, + symbol.SBTC: 0.1, + symbol.SUPER: 0.002, + symbol.SRND: 0.002, + symbol.STRB: 0.02, + symbol.M1: 0.002, + symbol.SPM: 0.01, + symbol.BUCKS: 0.002, + symbol.TOKEN: 0.01, + symbol.SWT: 0.05, + symbol.SWEET: 0.002, + symbol.SWING: 0.002, + symbol.CHSB: 10, + symbol.SIC: 0.002, + symbol.SDP: 0.002, + symbol.XSY: 0.002, + symbol.SYNX: 0.01, + symbol.SNRG: 0.002, + symbol.SYS: 0.002, + symbol.TAG: 0.01, + symbol.TAGR: 0.002, + symbol.TAJ: 0.01, + symbol.TAK: 0.01, + symbol.TAKE: 0.01, + symbol.TAM: 0.002, + symbol.XTO: 0.01, + symbol.TAP: 0.01, + symbol.TLE: 0.01, + symbol.TSE: 0.01, + symbol.TLEX: 0.01, + symbol.TAXI: 10, + symbol.TCN: 0.01, + symbol.TDFB: 0.002, + symbol.TEAM: 0.01, + symbol.TECH: 0.01, + symbol.TEC: 0.002, + symbol.TEK: 0.002, + symbol.TB: 0.002, + symbol.TLX: 10, + symbol.TELL: 0.01, + symbol.TENNET: 0.002, + symbol.PAY: 0.002, + symbol.TES: 0.002, + symbol.TRA: 0.01, + symbol.TGS: 10, + symbol.XVE: 0.01, + symbol.TCR: 0.01, + symbol.GCC: 0.002, + symbol.MAY: 0.01, + symbol.THOM: 0.01, + symbol.TIA: 0.002, + symbol.TIDE: 0.01, + symbol.TNT: 0.05, + symbol.TIE: 10, + symbol.TIT: 0.002, + symbol.TTC: 0.002, + symbol.TODAY: 0.01, + symbol.TBX: 10, + symbol.TKN: 0.01, + symbol.TDS: 10, + symbol.TLOSH: 0.01, + symbol.TOKC: 0.01, + symbol.TMRW: 0.01, + symbol.TOOL: 0.01, + symbol.TCX: 0.002, + symbol.TOT: 0.01, + symbol.TX: 0.002, + symbol.TRANSF: 0.002, + symbol.TRAP: 0.01, + symbol.TBCX: 0.01, + symbol.TRICK: 0.002, + symbol.TPG: 0.01, + symbol.TRX: 300, + symbol.TFL: 10, + symbol.TRUMP: 0.002, + symbol.TNG: 0.002, + symbol.TUR: 0.002, + symbol.TWERK: 0.002, + symbol.TWIST: 0.002, + symbol.TWO: 0.002, + symbol.UCASH: 10, + symbol.UAE: 0.01, + symbol.XBU: 0.01, + symbol.UBQ: 0.01, + symbol.U: 0.002, + symbol.UDOWN: 0.01, + symbol.GAIN: 0.01, + symbol.USC: 0.01, + symbol.UMC: 0.1, + symbol.UNF: 0.01, + symbol.UNIFY: 0.01, + symbol.UKG: 10, + symbol.USDE: 0.01, + symbol.UBTC: 0.1, + symbol.UIS: 0.01, + symbol.UNIT: 0.002, + symbol.UNI: 0.01, + symbol.UXC: 0.01, + symbol.URC: 0.002, + symbol.XUP: 0.002, + symbol.UFR: 10, + symbol.URO: 0.002, + symbol.UTLE: 0.002, + symbol.VAL: 0.02, + symbol.VPRC: 0.01, + symbol.VAPOR: 0.002, + symbol.VCOIN: 0.002, + symbol.VEC: 0.002, + symbol.VEC2: 0.01, + symbol.VLT: 0.01, + symbol.VENE: 0.01, + symbol.VNTX: 0.01, + symbol.VTN: 0.002, + symbol.XVG: 0.002, + symbol.CRED: 10, + symbol.VERS: 0.01, + symbol.VTC: 0.2, + symbol.VTX: 0.002, + symbol.VIA: 0.002, + symbol.VTY: 0.01, + symbol.VIP: 0.002, + symbol.VISIO: 0.01, + symbol.VK: 2, + symbol.VOL: 0.002, + symbol.VOYA: 0.002, + symbol.VPN: 0.002, + symbol.VSL: 0.01, + symbol.XVS: 0.01, + symbol.VTL: 0.01, + symbol.VULC: 0.01, + symbol.VVI: 10, + symbol.WGR: 0.01, + symbol.WAM: 0.01, + symbol.WARP: 0.002, + symbol.WASH: 0.01, + symbol.WAVES: 0.002, + symbol.WGO: 0.01, + symbol.WAY: 0.01, + symbol.WCASH: 0.01, + symbol.WEALTH: 0.002, + symbol.WEEK: 0.01, + symbol.WHO: 0.5, + symbol.WIC: 0.05, + symbol.WBB: 0.002, + symbol.WINE: 0.01, + symbol.WINK: 0.01, + symbol.WISC: 0.01, + symbol.WITCH: 0.01, + symbol.WMC: 0.01, + symbol.WOMEN: 0.01, + symbol.WOK: 0.01, + symbol.WRC: 10, + symbol.WRT: 0.01, + symbol.XCO: 0.002, + symbol.X2: 0.002, + symbol.XNX: 0.002, + symbol.XAU: 0.002, + symbol.XAV: 0.01, + symbol.XDE2: 0.002, + symbol.XDE: 0.002, + symbol.XIOS: 0.01, + symbol.XOC: 0.01, + symbol.XSSX: 0.002, + symbol.XBY: 0.01, + symbol.YAC: 0.01, + symbol.YMC: 0.01, + symbol.YAY: 0.01, + symbol.YBC: 0.002, + symbol.YES: 0.01, + symbol.YOB2X: 0.01, + symbol.YOVI: 0.002, + symbol.ZYD: 0.01, + symbol.ZEC: 0.02, + symbol.ZECD: 0.01, + symbol.ZEIT: 0.002, + symbol.ZENI: 0.01, + symbol.ZET2: 0.002, + symbol.ZET: 0.002, + symbol.ZMC: 0.002, + symbol.ZIRK: 0.002, + symbol.ZLQ: 0.01, + symbol.ZNE: 0.01, + symbol.ZONTO: 0.05, + symbol.ZOOM: 0.002, + symbol.ZRC: 0.01, + symbol.ZUR: 0.002, +} diff --git a/exchanges/yobit/yobit_wrapper.go b/exchanges/yobit/yobit_wrapper.go index 6ee2dbc9..9a07d16e 100644 --- a/exchanges/yobit/yobit_wrapper.go +++ b/exchanges/yobit/yobit_wrapper.go @@ -193,3 +193,8 @@ func (y *Yobit) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Curre func (y *Yobit) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (y *Yobit) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return y.GetFee(feeBuilder) +} diff --git a/exchanges/zb/zb.go b/exchanges/zb/zb.go index 47b389af..51e7220a 100644 --- a/exchanges/zb/zb.go +++ b/exchanges/zb/zb.go @@ -343,3 +343,28 @@ func (z *ZB) SendAuthenticatedHTTPRequest(method, endpoint string, values url.Va true, z.Verbose) } + +// GetFee returns an estimate of fee based on type of transaction +func (z *ZB) GetFee(feeBuilder exchange.FeeBuilder) (float64, error) { + var fee float64 + switch feeBuilder.FeeType { + case exchange.CryptocurrencyTradeFee: + fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount) + case exchange.CryptocurrencyWithdrawalFee: + fee = getWithdrawalFee(feeBuilder.FirstCurrency) + } + if fee < 0 { + fee = 0 + } + + return fee, nil +} + +func calculateTradingFee(purchasePrice, amount float64) (fee float64) { + fee = 0.002 + return fee * amount * purchasePrice +} + +func getWithdrawalFee(currency string) float64 { + return WithdrawalFees[currency] +} diff --git a/exchanges/zb/zb_test.go b/exchanges/zb/zb_test.go index cc8f5807..f11ce0bd 100644 --- a/exchanges/zb/zb_test.go +++ b/exchanges/zb/zb_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/thrasher-/gocryptotrader/config" + "github.com/thrasher-/gocryptotrader/currency/symbol" + exchange "github.com/thrasher-/gocryptotrader/exchanges" ) // Please supply you own test keys here for due diligence testing. @@ -134,3 +136,95 @@ func TestGetSpotKline(t *testing.T) { t.Errorf("Test failed - ZB GetSpotKline: %s", err) } } + +func setFeeBuilder() exchange.FeeBuilder { + return exchange.FeeBuilder{ + Amount: 1, + Delimiter: "-", + FeeType: exchange.CryptocurrencyTradeFee, + FirstCurrency: symbol.LTC, + SecondCurrency: symbol.BTC, + IsMaker: false, + PurchasePrice: 1, + CurrencyItem: symbol.USD, + BankTransactionType: exchange.WireTransfer, + } +} + +func TestGetFee(t *testing.T) { + z.SetDefaults() + TestSetup(t) + var feeBuilder = setFeeBuilder() + + // CryptocurrencyTradeFee Basic + if resp, err := z.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Error(err) + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.0015), resp) + } + + // CryptocurrencyTradeFee High quantity + feeBuilder = setFeeBuilder() + feeBuilder.Amount = 1000 + feeBuilder.PurchasePrice = 1000 + if resp, err := z.GetFee(feeBuilder); resp != float64(2000) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(2000), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee IsMaker + feeBuilder = setFeeBuilder() + feeBuilder.IsMaker = true + if resp, err := z.GetFee(feeBuilder); resp != float64(0.002) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.002), resp) + t.Error(err) + } + + // CryptocurrencyTradeFee Negative purchase price + feeBuilder = setFeeBuilder() + feeBuilder.PurchasePrice = -1000 + if resp, err := z.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + // CryptocurrencyWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := z.GetFee(feeBuilder); resp != float64(0.005) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0.005), resp) + t.Error(err) + } + + // CryptocurrencyWithdrawalFee Invalid currency + feeBuilder = setFeeBuilder() + feeBuilder.FirstCurrency = "hello" + feeBuilder.FeeType = exchange.CryptocurrencyWithdrawalFee + if resp, err := z.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // CyptocurrencyDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.CyptocurrencyDepositFee + if resp, err := z.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankDepositFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankDepositFee + if resp, err := z.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } + + // InternationalBankWithdrawalFee Basic + feeBuilder = setFeeBuilder() + feeBuilder.FeeType = exchange.InternationalBankWithdrawalFee + feeBuilder.CurrencyItem = symbol.USD + if resp, err := z.GetFee(feeBuilder); resp != float64(0) || err != nil { + t.Errorf("Test Failed - GetFee() error. Expected: %f, Recieved: %f", float64(0), resp) + t.Error(err) + } +} diff --git a/exchanges/zb/zb_type.go b/exchanges/zb/zb_type.go index 1ee81219..4f7648e1 100644 --- a/exchanges/zb/zb_type.go +++ b/exchanges/zb/zb_type.go @@ -1,6 +1,7 @@ package zb import "time" +import "github.com/thrasher-/gocryptotrader/currency/symbol" // OrderbookResponse holds the orderbook data for a symbol type OrderbookResponse struct { @@ -135,3 +136,77 @@ var ( TimeIntervalThreeDays = TimeInterval("3day") TimeIntervalWeek = TimeInterval("1week") ) + +// WithdrawalFees the large list of predefined withdrawal fees +// Prone to change, using highest value +var WithdrawalFees = map[string]float64{ + symbol.ZB: 5, + symbol.BTC: 0.001, + symbol.BCH: 0.0006, + symbol.LTC: 0.005, + symbol.ETH: 0.01, + symbol.ETC: 0.01, + symbol.BTS: 3, + symbol.EOS: 0.1, + symbol.QTUM: 0.01, + symbol.HC: 0.001, + symbol.XRP: 0.1, + symbol.QC: 5, + symbol.DASH: 0.002, + symbol.BCD: 0, + symbol.UBTC: 0.001, + symbol.SBTC: 0, + symbol.INK: 60, + symbol.BTH: 0.01, + symbol.LBTC: 0.01, + symbol.CHAT: 20, + symbol.BITCNY: 20, + symbol.HLC: 100, + symbol.BTP: 0.001, + symbol.TOPC: 200, + symbol.ENT: 50, + symbol.BAT: 40, + symbol.FIRST: 30, + symbol.SAFE: 0.001, + symbol.QUN: 200, + symbol.BTN: 0.005, + symbol.TRUE: 5, + symbol.CDC: 1, + symbol.DDM: 1, + symbol.HOTC: 150, + symbol.USDT: 5, + symbol.XUC: 1, + symbol.EPC: 40, + symbol.BDS: 3, + symbol.GRAM: 5, + symbol.DOGE: 20, + symbol.NEO: 0, + symbol.OMG: 0.5, + symbol.BTM: 4, + symbol.SNT: 60, + symbol.AE: 3, + symbol.ICX: 3, + symbol.ZRX: 10, + symbol.EDO: 4, + symbol.FUN: 250, + symbol.MANA: 70, + symbol.RCN: 70, + symbol.MCO: 0.6, + symbol.MITH: 10, + symbol.KNC: 5, + symbol.XLM: 0.1, + symbol.GNT: 20, + symbol.MTL: 3, + symbol.SUB: 20, + symbol.XEM: 4, + symbol.EOSDAC: 0, + symbol.KAN: 350, + symbol.AAA: 1, + symbol.XWC: 1, + symbol.PDX: 1, + symbol.SLT: 100, + symbol.ADA: 1, + symbol.HPY: 100, + symbol.PAX: 5, + symbol.XTZ: 0.1, +} diff --git a/exchanges/zb/zb_wrapper.go b/exchanges/zb/zb_wrapper.go index d67ffbd1..cdde5810 100644 --- a/exchanges/zb/zb_wrapper.go +++ b/exchanges/zb/zb_wrapper.go @@ -189,3 +189,8 @@ func (z *ZB) WithdrawFiatExchangeFundsToInternationalBank(currency pair.Currency func (z *ZB) GetWebsocket() (*exchange.Websocket, error) { return nil, errors.New("not yet implemented") } + +// GetFeeByType returns an estimate of fee based on type of transaction +func (z *ZB) GetFeeByType(feeBuilder exchange.FeeBuilder) (float64, error) { + return z.GetFee(feeBuilder) +} diff --git a/testdata/configtest.json b/testdata/configtest.json index 2a4aa7f0..6d0efd52 100644 --- a/testdata/configtest.json +++ b/testdata/configtest.json @@ -1,6 +1,6 @@ { "name": "", - "encryptConfig": 0, + "encryptConfig": -1, "globalHTTPTimeout": 15000000000, "currencyConfig": { "forexProviders": [