Skip to content

Commit 5eba4f7

Browse files
committed
Bug fixes, optimizations, code cleanup.
Issues with Mimic market identifiers for underlieing data exchanges seem to have finally been resolved. Mimic wallet flipping between long/short seems to be working as expected. Added program to set wallet to any value. Simulated deposits/withdrawals. Can also be used to reset a currency. Added program to graph exposure for Mimic wallets. Version Update. Changes to be committed: modified: Base/DSR-PlaceOrder modified: Base/JackrabbitLocker modified: Base/JackrabbitOliverTwist modified: Base/JackrabbitRelay modified: Base/Library/JRRmimic.py modified: Base/Library/JackrabbitProxy.py modified: Base/Library/JackrabbitRelay.py new file: Extras/Mimic/WalletExposure new file: Extras/Mimic/WalletSet renamed: Extras/OliverTwist/otEquity -> Extras/OliverTwist/OliverTwistEquity renamed: Extras/OliverTwist/otTrades -> Extras/OliverTwist/OliverTwistTrades
1 parent 85a4a28 commit 5eba4f7

12 files changed

+517
-18
lines changed

Base/Conditional.mimic.save

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
# MIMIC Conditional Orders - OliverTwist
5+
6+
# Jackrabbit Relay
7+
# 2021 Copyright © Robert APM Darin
8+
# All rights reserved unconditionally.
9+
10+
# This is the framework used by OliverTwist to process conditional orders.
11+
12+
# Pull the actual fill price from open trades
13+
14+
# SellAction of Close sets Units to all and closes the entire position. Sell uses the Oanda ticket system.
15+
16+
import sys
17+
sys.path.append('/home/GitHub/JackrabbitRelay/Base/Library')
18+
import os
19+
import json
20+
import time
21+
22+
import JRRsupport
23+
import JackrabbitRelay as JRR
24+
25+
# Timeout before Locker auto-deletes this order result
26+
27+
OliverTwistTimeout=(15*60)
28+
29+
# Write the result to Locker memory so parent knows we are finished.
30+
31+
def FinishOrphan(Key,lID,mID,State):
32+
# Get the lock read and set up the memory key. Locker doesn't care and which class this order belongs to. OliverTwist will
33+
# match the ID to the right class list, orphan or conditional.
34+
35+
OliverTwistLock=JRRsupport.Locker("OliverTwist",ID=lID)
36+
Memory=JRRsupport.Locker(Key,ID=mID)
37+
38+
State=State.lower()
39+
40+
if State!='delete':
41+
NewState='Waiting'
42+
else:
43+
NewState='Delete'
44+
45+
OliverTwistLock.Lock()
46+
Memory.Put(OliverTwistTimeout*100,NewState)
47+
OliverTwistLock.Unlock()
48+
49+
# We're done. This child has completed its task
50+
print(Key,NewState)
51+
sys.stdout.flush()
52+
sys.exit(0)
53+
54+
# Get the order ID. If there isn't an ID, the order FAILED.
55+
56+
def GetOrderID(res):
57+
try:
58+
if res.find('Order Confirmation ID')>-1:
59+
s=res.find('ID:')+4
60+
for e in range(s,len(res)):
61+
if res[e]=='\n':
62+
break
63+
oid=res[s:e]
64+
65+
return oid
66+
except:
67+
pass
68+
return None
69+
70+
# Calculate Proce Exit
71+
72+
def CalculatePriceExit(order,ts,dir,price):
73+
# Figure out TakeProfit or Stoploss
74+
if ts=='TakeProfit':
75+
if '%' in str(order[ts]):
76+
if dir=='long':
77+
val=price+((float(order[ts].replace('%','').strip())/100)*price)
78+
else:
79+
val=price-((float(order[ts].replace('%','').strip())/100)*price)
80+
# Pips
81+
elif 'p' in str(order[ts].lower()):
82+
if dir=='long':
83+
val=price+(float(order[ts].lower().replace('p','').strip())*0.0001)
84+
else:
85+
val=price-(float(order[ts].lower().replace('p','').strip())*0.0001)
86+
else:
87+
val=float(order[ts])
88+
elif ts=='StopLoss':
89+
if '%' in str(order[ts]):
90+
if dir=='long':
91+
val=price-((float(order[ts].replace('%','').strip())/100)*price)
92+
else:
93+
val=price+((float(order[ts].replace('%','').strip())/100)*price)
94+
# Pips
95+
elif 'p' in str(order[ts].lower()):
96+
if dir=='long':
97+
val=price-(float(order[ts].lower().replace('p','').strip())*0.0001)
98+
else:
99+
val=price+(float(order[ts].lower().replace('p','').strip())*0.0001)
100+
else:
101+
val=float(order[ts])
102+
103+
return val
104+
105+
###
106+
### Main driver
107+
###
108+
109+
def main():
110+
data=sys.stdin.read().strip()
111+
Orphan=json.loads(data)
112+
113+
# JRRsupport.WriteFile(f"/tmp/{Orphan['ID']}.txt",json.dumps(Orphan)+'\n')
114+
115+
# Use Relay to process and validate the order, must be a string
116+
if type(Orphan['Order']) is dict:
117+
order=json.dumps(Orphan['Order'])
118+
else:
119+
order=Orphan['Order']
120+
121+
relay=JRR.JackrabbitRelay(framework=Orphan['Framework'],payload=order,NoIdentityVerification=True)
122+
relay.JRLog.SetBaseName('OliverTwist')
123+
124+
###--->
125+
relay.JRLog.Write(f"{Orphan['Key']}: {Orphan['lID']} {Orphan['mID']}",stdOut=False)
126+
relay.JRLog.Write(f"{data}",stdOut=False)
127+
128+
try:
129+
# Check to see if order is still open and return current state
130+
# Handle OANDa's weird order id sequencing
131+
id=Orphan['ID']
132+
saction=relay.Order['SellAction'].lower()
133+
if type(Orphan['Response']) is str:
134+
Orphan['Response']=json.loads(Orphan['Response'])
135+
oDetail=Orphan['Response']
136+
137+
# Manage average and close extire position. Average and price are the same.
138+
price=float(oDetail['Price'])
139+
amount=float(oDetail['Amount'])
140+
141+
# Process the position
142+
143+
# We need to check TakeProfit and StopLoss. If one of them is hit, we need to build and order and
144+
# backfeed it in to Relay. It will place a new order.
145+
146+
# Get the direction of the trade, long/short
147+
dir=relay.Order['Direction'].lower()
148+
# Get Ticker
149+
ticker=relay.GetTicker(symbol=relay.Order['Asset'])
150+
151+
# Check to see if we have enough balance, if not then delete this order. Deal with futures as well.
152+
153+
base=relay.Markets[relay.Order['Asset']]['base'].upper()
154+
bal=relay.GetBalance(Base=base)
155+
156+
# Fsilsafe, in the WORST way possible. Do NOT leave a take profit out of the order. At this stage, the
157+
# whole thing is an absolute nightmare to fix. The is a very brutal way of dealing with poor user
158+
# choices.
159+
160+
if 'TakeProfit' not in relay.Order:
161+
relay.Order['TakeProfit']='2%'
162+
163+
# Calculate Take Profit
164+
tp=round(CalculatePriceExit(relay.Order,'TakeProfit',dir,price),5)
165+
166+
# Figure out StopLoss, if there is one
167+
if 'StopLoss' in relay.Order:
168+
sl=round(CalculatePriceExit(relay.Order,'StopLoss',dir,price),5)
169+
170+
# Get the "strikePrice". This handles both TakeProfit and StopLoss. It doesn't matter which as both are processed
171+
# the same way.
172+
173+
LogMSG=None
174+
StrikeHappened=False
175+
if dir=='long':
176+
if 'Diagnostics' in relay.Active:
177+
relay.JRLog.Write(f"{id}: {dir} Price: {price}, Bid: {ticker['Bid']} TP: {tp}/{relay.Order['TakeProfit']}, SL {sl}/{relay.Order['StopLoss']}",stdOut=False)
178+
179+
if ticker['Bid']>tp:
180+
profit=round((amount*ticker['Bid'])-(amount*price),8)
181+
LogMSG=f"{id}: TP {dir} hit: {tp}, {amount}: {price:.5f} -> {ticker['Bid']:5f}/{profit}"
182+
if 'StopLoss' in relay.Order and ticker['Bid']<sl:
183+
loss=round((amount*price)-(amount*ticker['Bid']),8)
184+
LogMSG=f"{id}: SL {dir} hit: {sl}, {amount}: {price:.5f} -> {ticker['Bid']:5f}/{loss}"
185+
186+
if ticker['Bid']>tp or ('StopLoss' in relay.Order and ticker['Bid']<sl):
187+
strikePrice=ticker['Bid']
188+
StrikeHappened=True
189+
else:
190+
if 'Diagnostics' in relay.Active:
191+
relay.JRLog.Write(f"{id}: {dir} Price: {price}, Ask: {ticker['Ask']} TP: {tp}/{relay.Order['TakeProfit']}, SL {sl}/{relay.Order['StopLoss']}",stdOut=False)
192+
193+
if ticker['Ask']<tp:
194+
profit=round((amount*price)-(amount*ticker['Ask']),8)
195+
LogMSG=f"{id}: TP {dir} hit: {tp}, {amount}: {price:.5f} -> {ticker['Ask']:5f}/{profit}"
196+
if 'StopLoss' in relay.Order and ticker['Ask']>sl:
197+
loss=round((amount*ticker['Ask'])-(amounts*price),8)
198+
LogMSG=f"{id}: SL {dir} hit: {sl}, {amount}: {price:.5f} -> {ticker['Ask']:5f}/{loss}"
199+
200+
if ticker['Ask']<tp or ('StopLoss' in relay.Order and ticker['Ask']>sl):
201+
strikePrice=ticker['Ask']
202+
StrikeHappened=True
203+
204+
if StrikeHappened==True:
205+
if abs(amount)>bal:
206+
# Build "strike" order. TakeProfit or StopLoss has been triggered
207+
newOrder={}
208+
newOrder['OliverTwist']='Conditional'
209+
newOrder['Exchange']=relay.Order['Exchange']
210+
newOrder['Account']=relay.Order['Account']
211+
newOrder['Market']=relay.Order['Market']
212+
newOrder['Asset']=relay.Order['Asset']
213+
newOrder['Action']=relay.Order['SellAction']
214+
newOrder['Price']=str(strikePrice)
215+
newOrder['Base']=str(amount)
216+
if 'OrderType' in relay.Order:
217+
newOrder['OrderType']=relay.Order['OrderType']
218+
else:
219+
newOrder['OrderType']='market'
220+
221+
# relay.JRLog.Write(f"{id}: {json.dumps(newOrder)}",stdOut=False)
222+
223+
newOrder['Identity']=relay.Active['Identity']
224+
225+
# Feed the new order to Relay
226+
result=relay.SendWebhook(newOrder)
227+
oid=GetOrderID(result)
228+
if oid!=None:
229+
relay.JRLog.Write(LogMSG,stdOut=False)
230+
resp=relay.GetOrderDetails(id=oid,symbol=relay.Order['Asset'])
231+
# Order must be closed as it succedded
232+
newOrder['ID']=oid
233+
relay.WriteLedger(Order=newOrder,Response=resp)
234+
FinishOrphan(Orphan['Key'],Orphan['lID'],Orphan['mID'],'Delete')
235+
else:
236+
# Give OliverTwist a response
237+
relay.JRLog.Write(f"{id}: Order failed",stdOut=False)
238+
FinishOrphan(Orphan['Key'],Orphan['lID'],Orphan['mID'],Orphan['Status'])
239+
else:
240+
# Amount < Balance
241+
FinishOrphan(Orphan['Key'],Orphan['lID'],Orphan['mID'],Orphan['Status'])
242+
else:
243+
# Strike did not happen
244+
FinishOrphan(Orphan['Key'],Orphan['lID'],Orphan['mID'],Orphan['Status'])
245+
except Exception as e:
246+
# Something went wrong
247+
relay.JRLog.Write(f"{Orphan['Key']}: Code Error - {sys.exc_info()[-1].tb_lineno}/{str(e)}",stdOut=False)
248+
if 'Diagnostics' in relay.Active:
249+
relay.JRLog.Write(f"{Orphan['Key']}: {data}",stdOut=False)
250+
FinishOrphan(Orphan['Key'],Orphan['lID'],Orphan['mID'],Orphan['Status'])
251+
252+
if __name__ == '__main__':
253+
main()

Base/DSR-PlaceOrder

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ def main():
4141

4242
# Validate these items in the order
4343

44+
if 'Market' in relay.Order:
45+
relay.Order['Market']=relay.Order['Market'].lower()
4446
if 'Recipe' not in relay.Order:
4547
relay.JRLog.Error("DSR","Recipe missing")
4648
if 'TCycles' not in relay.Order:

Base/JackrabbitLocker

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import json
2828

2929
import JRRsupport
3030

31-
Version="0.0.0.1.790"
31+
Version="0.0.0.1.800"
3232
BaseDirectory='/home/JackrabbitRelay2/Base'
3333
ConfigDirectory='/home/JackrabbitRelay2/Config'
3434
LogDirectory="/home/JackrabbitRelay2/Logs"

Base/JackrabbitOliverTwist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import subprocess
4040
import JRRsupport
4141
import JackrabbitRelay as JRR
4242

43-
Version="0.0.0.1.790"
43+
Version="0.0.0.1.800"
4444
BaseDirectory='/home/JackrabbitRelay2/Base'
4545
DataDirectory='/home/JackrabbitRelay2/Data'
4646
ConfigDirectory='/home/JackrabbitRelay2/Config'

Base/JackrabbitRelay

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import json
1616

1717
import JRRsupport
1818

19-
Version="0.0.0.1.790"
19+
Version="0.0.0.1.800"
2020
BaseDirectory='/home/JackrabbitRelay2/Base'
2121
ConfigDirectory='/home/JackrabbitRelay2/Config'
2222
LogDirectory="/home/JackrabbitRelay2/Logs"

Base/Library/JRRmimic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class mimic:
4545
# placed in init and released at exit.
4646

4747
def __init__(self,Exchange,Config,Active,DataDirectory=None):
48-
self.Version="0.0.0.1.790"
48+
self.Version="0.0.0.1.800"
4949

5050
self.StableCoinUSD=['USDT','USDC','BUSD','UST','DAI','FRAX','TUSD', \
5151
'USDP','LUSD','USDN','HUSD','FEI','TRIBE','RSR','OUSD','XSGD', \

Base/Library/JackrabbitProxy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
class JackrabbitProxy:
5151
def __init__(self,framework=None,payload=None,exchange=None,account=None,asset=None,Usage=None):
5252
# All the default locations
53-
self.Version="0.0.0.1.790"
53+
self.Version="0.0.0.1.800"
5454
self.BaseDirectory='/home/JackrabbitRelay2/Base'
5555
self.ConfigDirectory='/home/JackrabbitRelay2/Config'
5656
self.DataDirectory="/home/JackrabbitRelay2/Data"

Base/Library/JackrabbitRelay.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def Success(self,f,s):
106106
class JackrabbitRelay:
107107
def __init__(self,framework=None,payload=None,exchange=None,account=None,asset=None,secondary=None,NoIdentityVerification=False,Usage=None):
108108
# All the default locations
109-
self.Version="0.0.0.1.790"
109+
self.Version="0.0.0.1.800"
110110
self.NOhtml='<html><title>NO!</title><body style="background-color:#ffff00;display:flex;weight:100vw;height:100vh;align-items:center;justify-content:center"><h1 style="color:#ff0000;font-weight:1000;font-size:10rem">NO!</h1></body></html>'
111111
self.BaseDirectory='/home/JackrabbitRelay2/Base'
112112
self.ConfigDirectory='/home/JackrabbitRelay2/Config'
@@ -511,7 +511,7 @@ def ProcessPayload(self,NewPayload=None):
511511
# Verify the payload based upon a specific broker requirements.
512512

513513
def VerifyPayload(self):
514-
if self.Framework=='ccxt':
514+
if self.Framework=='ccxt' or self.Framework=='mimic':
515515
if "Market" not in self.Order:
516516
self.JRLog.Error('Processing Payload','Missing market identifier')
517517
self.Order['Market']=self.Order['Market'].lower()

0 commit comments

Comments
 (0)