Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pnl bands #947

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"start:server": "serve tests/test-dapp -p 3000",
"test:e2e:ci": "start-server-and-test 'yarn start:server' http-get://localhost:3000 'yarn run cypress'",
"codegen:uniswap": "apollo client:codegen --target typescript --globalTypesFile=types/global_apollo.ts --includes=./src/queries/uniswap/**/*.ts --endpoint 'https://api.thegraph.com/subgraphs/name/kmkoushik/uniswap-v3-ropsten'",
"codegen:squeeth": "apollo client:codegen --target typescript --globalTypesFile=types/global_apollo.ts --includes=./src/queries/squeeth/**/*.ts --endpoint 'https://api.thegraph.com/subgraphs/name/haythem96/squeeth-temp-subgraph'"
"codegen:squeeth": "apollo client:codegen --target typescript --globalTypesFile=types/global_apollo.ts --includes=./src/queries/squeeth/**/*.ts --endpoint 'https://api.thegraph.com/subgraphs/name/opynfinance/squeeth'"
},
"dependencies": {
"@amplitude/analytics-browser": "^1.3.0",
Expand Down Expand Up @@ -114,4 +114,4 @@
"resolutions": {
"assemblyscript": "git+https://github.com/AssemblyScript/assemblyscript.git#v0.6"
}
}
}
24 changes: 24 additions & 0 deletions packages/frontend/pages/api/getBlockNumber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import axios from 'axios'
import type { NextApiRequest, NextApiResponse } from 'next'

const ETHERSCAN_API = 'https://api.etherscan.io'

const handleRequest = async (req: NextApiRequest, res: NextApiResponse) => {
const { timestamp } = req.query
// eslint-disable-next-line no-undef
console.log('timestamp', timestamp)
const resp = await fetch(
`${ETHERSCAN_API}/api?module=block&action=getblocknobytime&timestamp=${Number(timestamp).toFixed(
0,
)}&closest=before&apikey=${process.env.ETHERSCAN_API_KEY}`,
)
const data = await resp.json()
console.log('data', data)
if (data.status === '1') {
return res.status(200).json({ blockNumber: Number(data.result) })
} else {
return res.status(200).json({ blockNumber: 0 })
}
}

export default handleRequest
3 changes: 2 additions & 1 deletion packages/frontend/pages/strategies/crab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import CrabTradeV2 from '@components/Strategies/Crab/CrabTradeV2'
import MyPosition from '@components/Strategies/Crab/MyPosition'
import About from '@components/Strategies/Crab/About'
import StrategyPerformance from '@components/Strategies/Crab/StrategyPerformance'
import { useSetStrategyDataV2, useCurrentCrabPositionValueV2 } from '@state/crab/hooks'
import { useSetStrategyDataV2, useCurrentCrabPositionValueV2, useCrabProfitData } from '@state/crab/hooks'
import { useInitCrabMigration } from '@state/crabMigration/hooks'
import { SQUEETH_BASE_URL } from '@constants/index'
import { useGetVault } from '@state/controller/hooks'

const useStyles = makeStyles((theme) =>
createStyles({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import { toTokenAmount } from '@utils/calculations'
import { useOnChainETHPrice } from '@hooks/useETHPrice'
import { formatNumber } from '@utils/formatter'
import useStyles from '@components/Strategies/styles'
import { useBullProfitData } from '@state/bull/hooks'
import { getNextHedgeDate } from '@state/crab/utils'
import { getBullExcessProfitDataPoints } from '@utils/strategyPayoff'

const useTooltipStyles = makeStyles(() => ({
root: {
Expand Down Expand Up @@ -61,7 +64,7 @@ const CustomTooltip: React.FC<CustomTooltipProps> = ({ active, payload, ethPrice
</Typography>
<Typography className={classes.value}>
{`Zen Bull return: `}
<b>{formatNumber(strategyReturn)}%</b>
<b>{formatNumber(strategyReturn, 4)}%</b>
</Typography>
</div>
)
Expand Down Expand Up @@ -100,51 +103,51 @@ function getStrategyReturn(funding: number, ethReturn: number) {
return (funding - Math.pow(ethReturn, 2)) * 100 * 0.5
}

// generate data from -percentRange to +percentRange
const getDataPoints = (funding: number, ethPriceAtLastHedge: number, percentRange: number) => {
const dataPoints = []

const starting = new BigNumber(-percentRange)
const increment = new BigNumber(0.05)
const ending = new BigNumber(percentRange)

let current = starting
while (current.lte(ending)) {
const ethReturn = current.div(100).toNumber()

const strategyReturn = getStrategyReturn(funding, ethReturn)
const strategyReturnPositive = strategyReturn >= 0 ? strategyReturn : null
const strategyReturnNegative = strategyReturn < 0 ? strategyReturn : null

dataPoints.push({
ethPrice: ethPriceAtLastHedge + ethReturn * ethPriceAtLastHedge,
strategyReturn,
strategyReturnPositive,
strategyReturnNegative,
})

current = current.plus(increment)
}

return dataPoints
}

const Chart: React.FC<{ currentFunding: number }> = ({ currentFunding }) => {
const ethPriceAtLastHedgeValue = useAtomValue(ethPriceAtLastHedgeAtomV2)
const ethPrice = useOnChainETHPrice()
const { profitData } = useBullProfitData()

const impliedFunding = 2 * currentFunding // for 2 days
const ethPriceAtLastHedge = Number(toTokenAmount(ethPriceAtLastHedgeValue, 18))
const currentEthPrice = Number(ethPrice)

const profitableBoundsPercent = Math.sqrt(impliedFunding)
const lowerPriceBandForProfitability = ethPriceAtLastHedge - profitableBoundsPercent * ethPriceAtLastHedge
const upperPriceBandForProfitability = ethPriceAtLastHedge + profitableBoundsPercent * ethPriceAtLastHedge

const data = useMemo(() => {
const percentRange = profitableBoundsPercent * 4 * 100 // 4x the profitable move percent
return getDataPoints(impliedFunding, ethPriceAtLastHedge, percentRange)
}, [impliedFunding, ethPriceAtLastHedge, profitableBoundsPercent])
const {
dataPoints: data,
lowerPriceBandForProfitability,
upperPriceBandForProfitability,
currentProfit,
} = useMemo(() => {
const nextHedgeTime = getNextHedgeDate(new Date(profitData.time * 1000)).getTime()
const timeUntilNextHedge = nextHedgeTime - new Date(profitData.time * 1000).getTime()
console.log('timeUntilNextHedge', timeUntilNextHedge)
return getBullExcessProfitDataPoints(
profitData.ethPriceAtHedge,
profitData.nf,
profitData.shortAmt,
profitData.collat,
profitData.oSqthPrice,
30,
currentEthPrice,
2,
profitData.eulerEth,
profitData.ethSupplyApy,
profitData.eulerUsdc,
profitData.usdcBorrowApy,
)
}, [
currentEthPrice,
profitData.collat,
profitData.ethPriceAtHedge,
profitData.ethSupplyApy,
profitData.eulerEth,
profitData.eulerUsdc,
profitData.nf,
profitData.oSqthPrice,
profitData.shortAmt,
profitData.time,
profitData.usdcBorrowApy,
])

const getStrategyReturnForETHPrice = (ethPriceValue: number) => {
const ethReturn = (ethPriceValue - ethPriceAtLastHedge) / ethPriceAtLastHedge
Expand Down Expand Up @@ -204,7 +207,7 @@ const Chart: React.FC<{ currentFunding: number }> = ({ currentFunding }) => {
type="number"
dataKey="strategyReturn"
tick={false}
domain={isMobileBreakpoint ? ['dataMin - 1.25', 'dataMax + 1.25'] : ['dataMin - 0.5', 'dataMax + 0.5']}
domain={isMobileBreakpoint ? ['dataMin - .25', 'dataMax + .25'] : ['dataMin - 0.05', 'dataMax + 0.05']}
strokeDasharray="5,5"
strokeOpacity="0.5"
stroke="#fff"
Expand All @@ -221,14 +224,14 @@ const Chart: React.FC<{ currentFunding: number }> = ({ currentFunding }) => {

<ReferenceArea
shape={<CandyBar />}
x1={lowerPriceBandForProfitability}
x2={upperPriceBandForProfitability}
x1={lowerPriceBandForProfitability.ethPrice}
x2={upperPriceBandForProfitability.ethPrice}
fill={successColor + '16'}
stroke={successColor}
/>

<Tooltip
wrapperStyle={{ outline: 'none' }}
wrapperStyle={{ outline: 'none', zIndex: 201 }}
cursor={{ stroke: '#fff', strokeOpacity: '0.5', strokeWidth: 1 }}
content={<CustomTooltip ethPriceAtLastHedge={ethPriceAtLastHedge} />}
/>
Expand All @@ -253,28 +256,28 @@ const Chart: React.FC<{ currentFunding: number }> = ({ currentFunding }) => {
/>

<ReferenceDot
x={lowerPriceBandForProfitability}
y={getStrategyReturnForETHPrice(lowerPriceBandForProfitability)}
x={lowerPriceBandForProfitability.ethPrice}
y={lowerPriceBandForProfitability.strategyReturn}
r={0}
>
<Label
fontFamily={'DM Mono'}
fontWeight={500}
value={'$' + formatNumber(lowerPriceBandForProfitability, 0)}
value={'$' + formatNumber(lowerPriceBandForProfitability.ethPrice, 0)}
position="insideBottomRight"
offset={8}
fill="#ffffffcc"
/>
</ReferenceDot>
<ReferenceDot
x={upperPriceBandForProfitability}
y={getStrategyReturnForETHPrice(upperPriceBandForProfitability)}
x={upperPriceBandForProfitability.ethPrice}
y={upperPriceBandForProfitability.strategyReturn}
r={0}
>
<Label
fontFamily={'DM Mono'}
fontWeight={500}
value={'$' + formatNumber(upperPriceBandForProfitability, 0)}
value={'$' + formatNumber(upperPriceBandForProfitability.ethPrice, 0)}
position="insideBottomLeft"
offset={8}
fill="#ffffffcc"
Expand All @@ -283,9 +286,9 @@ const Chart: React.FC<{ currentFunding: number }> = ({ currentFunding }) => {

<ReferenceDot
x={currentEthPrice}
y={currentStrategyReturn}
y={currentProfit.strategyReturn}
r={5}
fill={currentStrategyReturn < 0 ? errorColor : successColor}
fill={currentProfit.strategyReturn < 0 ? errorColor : successColor}
strokeWidth={0}
>
<Label
Expand All @@ -294,7 +297,7 @@ const Chart: React.FC<{ currentFunding: number }> = ({ currentFunding }) => {
value={'$' + formatNumber(currentEthPrice, 0)}
position="insideTop"
offset={20}
fill={currentStrategyReturn < 0 ? errorColor : successColor}
fill={currentProfit.strategyReturn < 0 ? errorColor : successColor}
filter="url(#removebackground)"
/>
</ReferenceDot>
Expand Down
112 changes: 107 additions & 5 deletions packages/frontend/src/components/Strategies/Bull/About/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import { Box, Typography } from '@material-ui/core'
import React, { useEffect, useState } from 'react'
import { Box, InputLabel, TextField, TextFieldProps, Typography } from '@material-ui/core'
import clsx from 'clsx'
import { makeStyles, createStyles } from '@material-ui/core/styles'

Expand All @@ -10,13 +10,40 @@ import useStyles from '@components/Strategies/styles'
import { LinkWrapper } from '@components/LinkWrapper'
import useAmplitude from '@hooks/useAmplitude'
import { SITE_EVENTS } from '@utils/amplitude'
import { DateTimePicker, MuiPickersUtilsProvider } from '@material-ui/pickers'
import DateFnsUtils from '@date-io/date-fns'
import { useAtom, useSetAtom } from 'jotai'
import { bullFirstDepositBlockAtom, bullFirstDepositTimestampAtom } from '@state/bull/atoms'

const useTextFieldStyles = makeStyles((theme) =>
createStyles({
labelRoot: {
color: '#8C8D8D',
fontSize: '14px',
fontWeight: 500,
},
inputRoot: {
padding: '10px 16px',
fontSize: '15px',
fontWeight: 500,
fontFamily: 'DM Mono',
width: '22ch',
border: '2px solid #303436',
borderRadius: '12px',
},
inputFocused: {
borderColor: theme.palette.primary.main,
},
}),
)

const useAboutStyles = makeStyles((theme) =>
createStyles({
timerContainer: {
position: 'absolute',
top: '10px',
right: '0',
zIndex: 200,

[theme.breakpoints.down('sm')]: {
position: 'relative',
Expand All @@ -25,11 +52,85 @@ const useAboutStyles = makeStyles((theme) =>
marginBottom: '16px',
},
},
dateContainer: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: '16px',
height: '40px',
},
label: {
fontSize: '15px',
color: 'rgba(255, 255, 255, 0.5)',
fontWeight: 500,
textAlign: 'right',
},
}),
)

const CustomTextField: React.FC<TextFieldProps> = ({ inputRef, label, InputProps, id, variant, ...props }) => {
const classes = useTextFieldStyles()

return (
<Box display="flex" flexDirection="column" gridGap="4px">
<InputLabel htmlFor={id} classes={{ root: classes.labelRoot }}>
{label}
</InputLabel>
<TextField
id={id}
InputProps={{
classes: {
root: classes.inputRoot,
focused: classes.inputFocused,
},
disableUnderline: true,
...InputProps,
}}
{...props}
/>
</Box>
)
}

const gitBookLink = 'https://opyn.gitbook.io/opyn-strategies/zen-bull/introduction'

const DepositTimePicker: React.FC = () => {
const aboutClasses = useAboutStyles()
const [depositTime, setDepositTime] = useAtom(bullFirstDepositTimestampAtom)
const setDepositBlock = useSetAtom(bullFirstDepositBlockAtom)
const [date, setDate] = useState(new Date(depositTime ? depositTime * 1000 : Date.now()))

useEffect(() => {
setDate(new Date(depositTime ? depositTime * 1000 : Date.now()))
}, [depositTime])

const onDepositDateChange = async (date: Date | null) => {
if (date) {
setDate(date)
setDepositTime(date.getTime() / 1000)
const resp = await fetch(`/api/getBlockNumber?timestamp=${date.getTime() / 1000}`)
const data = await resp.json()
setDepositBlock(data.blockNumber)
}
}

return (
<div>
<Typography className={aboutClasses.label}>Deposit date</Typography>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<DateTimePicker
fullWidth
value={date}
onChange={onDepositDateChange}
DialogProps={{ disableScrollLock: true }}
TextFieldComponent={CustomTextField}
format={'MM/dd/yy HH:mm a'}
maxDate={new Date()}
/>
</MuiPickersUtilsProvider>
</div>
)
}

const About: React.FC = () => {
const classes = useStyles()
const aboutClasses = useAboutStyles()
Expand All @@ -46,8 +147,8 @@ const About: React.FC = () => {
</Typography>

<Typography className={clsx(classes.text, classes.textMargin)}>
Zen bull makes money when ETH goes up, slow and steady. It stacks ETH if ETH is within the below bands at the
next rebalance.{' '}
Zen bull makes money when ETH goes up, slow and steady. It stacks ETH if ETH is within the below bands over
the period of 2 days.{' '}
<LinkWrapper
href={gitBookLink}
onClick={() => track(SITE_EVENTS.CLICK_LEARN_MORE_BULL, { link: gitBookLink })}
Expand All @@ -59,7 +160,8 @@ const About: React.FC = () => {

<Box position="relative" marginTop="32px">
<div className={aboutClasses.timerContainer}>
<NextRebalanceTimer />
<DepositTimePicker />
{/* <NextRebalanceTimer /> */}
</div>
<ProfitabilityChart />
</Box>
Expand Down
Loading